1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 20c0d06caSMauro Carvalho Chehab /* 30c0d06caSMauro Carvalho Chehab * Support for the sensor part which is integrated (I think) into the 40c0d06caSMauro Carvalho Chehab * st6422 stv06xx alike bridge, as its integrated there are no i2c writes 50c0d06caSMauro Carvalho Chehab * but instead direct bridge writes. 60c0d06caSMauro Carvalho Chehab * 70c0d06caSMauro Carvalho Chehab * Copyright (c) 2009 Hans de Goede <hdegoede@redhat.com> 80c0d06caSMauro Carvalho Chehab * 90c0d06caSMauro Carvalho Chehab * Strongly based on qc-usb-messenger, which is: 100c0d06caSMauro Carvalho Chehab * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher 110c0d06caSMauro Carvalho Chehab * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland 120c0d06caSMauro Carvalho Chehab * Copyright (c) 2002, 2003 Tuukka Toivonen 130c0d06caSMauro Carvalho Chehab */ 140c0d06caSMauro Carvalho Chehab 150c0d06caSMauro Carvalho Chehab #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 160c0d06caSMauro Carvalho Chehab 170c0d06caSMauro Carvalho Chehab #include "stv06xx_st6422.h" 180c0d06caSMauro Carvalho Chehab 190c0d06caSMauro Carvalho Chehab static struct v4l2_pix_format st6422_mode[] = { 200c0d06caSMauro Carvalho Chehab /* Note we actually get 124 lines of data, of which we skip the 4st 210c0d06caSMauro Carvalho Chehab 4 as they are garbage */ 220c0d06caSMauro Carvalho Chehab { 230c0d06caSMauro Carvalho Chehab 162, 240c0d06caSMauro Carvalho Chehab 120, 250c0d06caSMauro Carvalho Chehab V4L2_PIX_FMT_SGRBG8, 260c0d06caSMauro Carvalho Chehab V4L2_FIELD_NONE, 270c0d06caSMauro Carvalho Chehab .sizeimage = 162 * 120, 280c0d06caSMauro Carvalho Chehab .bytesperline = 162, 290c0d06caSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB, 300c0d06caSMauro Carvalho Chehab .priv = 1 310c0d06caSMauro Carvalho Chehab }, 320c0d06caSMauro Carvalho Chehab /* Note we actually get 248 lines of data, of which we skip the 4st 330c0d06caSMauro Carvalho Chehab 4 as they are garbage, and we tell the app it only gets the 340c0d06caSMauro Carvalho Chehab first 240 of the 244 lines it actually gets, so that it ignores 350c0d06caSMauro Carvalho Chehab the last 4. */ 360c0d06caSMauro Carvalho Chehab { 370c0d06caSMauro Carvalho Chehab 324, 380c0d06caSMauro Carvalho Chehab 240, 390c0d06caSMauro Carvalho Chehab V4L2_PIX_FMT_SGRBG8, 400c0d06caSMauro Carvalho Chehab V4L2_FIELD_NONE, 410c0d06caSMauro Carvalho Chehab .sizeimage = 324 * 244, 420c0d06caSMauro Carvalho Chehab .bytesperline = 324, 430c0d06caSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB, 440c0d06caSMauro Carvalho Chehab .priv = 0 450c0d06caSMauro Carvalho Chehab }, 460c0d06caSMauro Carvalho Chehab }; 470c0d06caSMauro Carvalho Chehab 480c0d06caSMauro Carvalho Chehab /* V4L2 controls supported by the driver */ 490c0d06caSMauro Carvalho Chehab static int setbrightness(struct sd *sd, s32 val); 500c0d06caSMauro Carvalho Chehab static int setcontrast(struct sd *sd, s32 val); 510c0d06caSMauro Carvalho Chehab static int setgain(struct sd *sd, u8 gain); 520c0d06caSMauro Carvalho Chehab static int setexposure(struct sd *sd, s16 expo); 530c0d06caSMauro Carvalho Chehab 540c0d06caSMauro Carvalho Chehab static int st6422_s_ctrl(struct v4l2_ctrl *ctrl) 550c0d06caSMauro Carvalho Chehab { 560c0d06caSMauro Carvalho Chehab struct gspca_dev *gspca_dev = 570c0d06caSMauro Carvalho Chehab container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 580c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *)gspca_dev; 590c0d06caSMauro Carvalho Chehab int err = -EINVAL; 600c0d06caSMauro Carvalho Chehab 610c0d06caSMauro Carvalho Chehab switch (ctrl->id) { 620c0d06caSMauro Carvalho Chehab case V4L2_CID_BRIGHTNESS: 630c0d06caSMauro Carvalho Chehab err = setbrightness(sd, ctrl->val); 640c0d06caSMauro Carvalho Chehab break; 650c0d06caSMauro Carvalho Chehab case V4L2_CID_CONTRAST: 660c0d06caSMauro Carvalho Chehab err = setcontrast(sd, ctrl->val); 670c0d06caSMauro Carvalho Chehab break; 680c0d06caSMauro Carvalho Chehab case V4L2_CID_GAIN: 690c0d06caSMauro Carvalho Chehab err = setgain(sd, ctrl->val); 700c0d06caSMauro Carvalho Chehab break; 710c0d06caSMauro Carvalho Chehab case V4L2_CID_EXPOSURE: 720c0d06caSMauro Carvalho Chehab err = setexposure(sd, ctrl->val); 730c0d06caSMauro Carvalho Chehab break; 740c0d06caSMauro Carvalho Chehab } 750c0d06caSMauro Carvalho Chehab 760c0d06caSMauro Carvalho Chehab /* commit settings */ 770c0d06caSMauro Carvalho Chehab if (err >= 0) 780c0d06caSMauro Carvalho Chehab err = stv06xx_write_bridge(sd, 0x143f, 0x01); 790c0d06caSMauro Carvalho Chehab sd->gspca_dev.usb_err = err; 800c0d06caSMauro Carvalho Chehab return err; 810c0d06caSMauro Carvalho Chehab } 820c0d06caSMauro Carvalho Chehab 830c0d06caSMauro Carvalho Chehab static const struct v4l2_ctrl_ops st6422_ctrl_ops = { 840c0d06caSMauro Carvalho Chehab .s_ctrl = st6422_s_ctrl, 850c0d06caSMauro Carvalho Chehab }; 860c0d06caSMauro Carvalho Chehab 870c0d06caSMauro Carvalho Chehab static int st6422_init_controls(struct sd *sd) 880c0d06caSMauro Carvalho Chehab { 890c0d06caSMauro Carvalho Chehab struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; 900c0d06caSMauro Carvalho Chehab 910c0d06caSMauro Carvalho Chehab v4l2_ctrl_handler_init(hdl, 4); 920c0d06caSMauro Carvalho Chehab v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, 930c0d06caSMauro Carvalho Chehab V4L2_CID_BRIGHTNESS, 0, 31, 1, 3); 940c0d06caSMauro Carvalho Chehab v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, 950c0d06caSMauro Carvalho Chehab V4L2_CID_CONTRAST, 0, 15, 1, 11); 960c0d06caSMauro Carvalho Chehab v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, 970c0d06caSMauro Carvalho Chehab V4L2_CID_EXPOSURE, 0, 1023, 1, 256); 980c0d06caSMauro Carvalho Chehab v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, 990c0d06caSMauro Carvalho Chehab V4L2_CID_GAIN, 0, 255, 1, 64); 1000c0d06caSMauro Carvalho Chehab 1010c0d06caSMauro Carvalho Chehab return hdl->error; 1020c0d06caSMauro Carvalho Chehab } 1030c0d06caSMauro Carvalho Chehab 1040c0d06caSMauro Carvalho Chehab static int st6422_probe(struct sd *sd) 1050c0d06caSMauro Carvalho Chehab { 1060c0d06caSMauro Carvalho Chehab if (sd->bridge != BRIDGE_ST6422) 1070c0d06caSMauro Carvalho Chehab return -ENODEV; 1080c0d06caSMauro Carvalho Chehab 1090c0d06caSMauro Carvalho Chehab pr_info("st6422 sensor detected\n"); 1100c0d06caSMauro Carvalho Chehab 1110c0d06caSMauro Carvalho Chehab sd->gspca_dev.cam.cam_mode = st6422_mode; 1120c0d06caSMauro Carvalho Chehab sd->gspca_dev.cam.nmodes = ARRAY_SIZE(st6422_mode); 1130c0d06caSMauro Carvalho Chehab return 0; 1140c0d06caSMauro Carvalho Chehab } 1150c0d06caSMauro Carvalho Chehab 1160c0d06caSMauro Carvalho Chehab static int st6422_init(struct sd *sd) 1170c0d06caSMauro Carvalho Chehab { 1180c0d06caSMauro Carvalho Chehab int err = 0, i; 1190c0d06caSMauro Carvalho Chehab 1200c0d06caSMauro Carvalho Chehab const u16 st6422_bridge_init[][2] = { 1210c0d06caSMauro Carvalho Chehab { STV_ISO_ENABLE, 0x00 }, /* disable capture */ 1220c0d06caSMauro Carvalho Chehab { 0x1436, 0x00 }, 1230c0d06caSMauro Carvalho Chehab { 0x1432, 0x03 }, /* 0x00-0x1F brightness */ 1240c0d06caSMauro Carvalho Chehab { 0x143a, 0xf9 }, /* 0x00-0x0F contrast */ 1250c0d06caSMauro Carvalho Chehab { 0x0509, 0x38 }, /* R */ 1260c0d06caSMauro Carvalho Chehab { 0x050a, 0x38 }, /* G */ 1270c0d06caSMauro Carvalho Chehab { 0x050b, 0x38 }, /* B */ 1280c0d06caSMauro Carvalho Chehab { 0x050c, 0x2a }, 1290c0d06caSMauro Carvalho Chehab { 0x050d, 0x01 }, 1300c0d06caSMauro Carvalho Chehab 1310c0d06caSMauro Carvalho Chehab 1320c0d06caSMauro Carvalho Chehab { 0x1431, 0x00 }, /* 0x00-0x07 ??? */ 1330c0d06caSMauro Carvalho Chehab { 0x1433, 0x34 }, /* 160x120, 0x00-0x01 night filter */ 1340c0d06caSMauro Carvalho Chehab { 0x1438, 0x18 }, /* 640x480 */ 1350c0d06caSMauro Carvalho Chehab /* 18 bayes */ 1360c0d06caSMauro Carvalho Chehab /* 10 compressed? */ 1370c0d06caSMauro Carvalho Chehab 1380c0d06caSMauro Carvalho Chehab { 0x1439, 0x00 }, 1390c0d06caSMauro Carvalho Chehab /* anti-noise? 0xa2 gives a perfect image */ 1400c0d06caSMauro Carvalho Chehab 1410c0d06caSMauro Carvalho Chehab { 0x143b, 0x05 }, 1420c0d06caSMauro Carvalho Chehab { 0x143c, 0x00 }, /* 0x00-0x01 - ??? */ 1430c0d06caSMauro Carvalho Chehab 1440c0d06caSMauro Carvalho Chehab 1450c0d06caSMauro Carvalho Chehab /* shutter time 0x0000-0x03FF */ 1460c0d06caSMauro Carvalho Chehab /* low value give good picures on moving objects (but requires much light) */ 1470c0d06caSMauro Carvalho Chehab /* high value gives good picures in darkness (but tends to be overexposed) */ 1480c0d06caSMauro Carvalho Chehab { 0x143e, 0x01 }, 1490c0d06caSMauro Carvalho Chehab { 0x143d, 0x00 }, 1500c0d06caSMauro Carvalho Chehab 1510c0d06caSMauro Carvalho Chehab { 0x1442, 0xe2 }, 1520c0d06caSMauro Carvalho Chehab /* write: 1x1x xxxx */ 1530c0d06caSMauro Carvalho Chehab /* read: 1x1x xxxx */ 1540c0d06caSMauro Carvalho Chehab /* bit 5 == button pressed and hold if 0 */ 1550c0d06caSMauro Carvalho Chehab /* write 0xe2,0xea */ 1560c0d06caSMauro Carvalho Chehab 1570c0d06caSMauro Carvalho Chehab /* 0x144a */ 1580c0d06caSMauro Carvalho Chehab /* 0x00 init */ 1590c0d06caSMauro Carvalho Chehab /* bit 7 == button has been pressed, but not handled */ 1600c0d06caSMauro Carvalho Chehab 1610c0d06caSMauro Carvalho Chehab /* interrupt */ 1620c0d06caSMauro Carvalho Chehab /* if(urb->iso_frame_desc[i].status == 0x80) { */ 1630c0d06caSMauro Carvalho Chehab /* if(urb->iso_frame_desc[i].status == 0x88) { */ 1640c0d06caSMauro Carvalho Chehab 1650c0d06caSMauro Carvalho Chehab { 0x1500, 0xd0 }, 1660c0d06caSMauro Carvalho Chehab { 0x1500, 0xd0 }, 1670c0d06caSMauro Carvalho Chehab { 0x1500, 0x50 }, /* 0x00 - 0xFF 0x80 == compr ? */ 1680c0d06caSMauro Carvalho Chehab 1690c0d06caSMauro Carvalho Chehab { 0x1501, 0xaf }, 1700c0d06caSMauro Carvalho Chehab /* high val-> light area gets darker */ 1710c0d06caSMauro Carvalho Chehab /* low val -> light area gets lighter */ 1720c0d06caSMauro Carvalho Chehab { 0x1502, 0xc2 }, 1730c0d06caSMauro Carvalho Chehab /* high val-> light area gets darker */ 1740c0d06caSMauro Carvalho Chehab /* low val -> light area gets lighter */ 1750c0d06caSMauro Carvalho Chehab { 0x1503, 0x45 }, 1760c0d06caSMauro Carvalho Chehab /* high val-> light area gets darker */ 1770c0d06caSMauro Carvalho Chehab /* low val -> light area gets lighter */ 1780c0d06caSMauro Carvalho Chehab { 0x1505, 0x02 }, 1790c0d06caSMauro Carvalho Chehab /* 2 : 324x248 80352 bytes */ 1800c0d06caSMauro Carvalho Chehab /* 7 : 248x162 40176 bytes */ 1810c0d06caSMauro Carvalho Chehab /* c+f: 162*124 20088 bytes */ 1820c0d06caSMauro Carvalho Chehab 1830c0d06caSMauro Carvalho Chehab { 0x150e, 0x8e }, 1840c0d06caSMauro Carvalho Chehab { 0x150f, 0x37 }, 1850c0d06caSMauro Carvalho Chehab { 0x15c0, 0x00 }, 1860c0d06caSMauro Carvalho Chehab { 0x15c3, 0x08 }, /* 0x04/0x14 ... test pictures ??? */ 1870c0d06caSMauro Carvalho Chehab 1880c0d06caSMauro Carvalho Chehab 1890c0d06caSMauro Carvalho Chehab { 0x143f, 0x01 }, /* commit settings */ 1900c0d06caSMauro Carvalho Chehab 1910c0d06caSMauro Carvalho Chehab }; 1920c0d06caSMauro Carvalho Chehab 1930c0d06caSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(st6422_bridge_init) && !err; i++) { 1940c0d06caSMauro Carvalho Chehab err = stv06xx_write_bridge(sd, st6422_bridge_init[i][0], 1950c0d06caSMauro Carvalho Chehab st6422_bridge_init[i][1]); 1960c0d06caSMauro Carvalho Chehab } 1970c0d06caSMauro Carvalho Chehab 1980c0d06caSMauro Carvalho Chehab return err; 1990c0d06caSMauro Carvalho Chehab } 2000c0d06caSMauro Carvalho Chehab 2010c0d06caSMauro Carvalho Chehab static int setbrightness(struct sd *sd, s32 val) 2020c0d06caSMauro Carvalho Chehab { 2030c0d06caSMauro Carvalho Chehab /* val goes from 0 -> 31 */ 2040c0d06caSMauro Carvalho Chehab return stv06xx_write_bridge(sd, 0x1432, val); 2050c0d06caSMauro Carvalho Chehab } 2060c0d06caSMauro Carvalho Chehab 2070c0d06caSMauro Carvalho Chehab static int setcontrast(struct sd *sd, s32 val) 2080c0d06caSMauro Carvalho Chehab { 2090c0d06caSMauro Carvalho Chehab /* Val goes from 0 -> 15 */ 2100c0d06caSMauro Carvalho Chehab return stv06xx_write_bridge(sd, 0x143a, val | 0xf0); 2110c0d06caSMauro Carvalho Chehab } 2120c0d06caSMauro Carvalho Chehab 2130c0d06caSMauro Carvalho Chehab static int setgain(struct sd *sd, u8 gain) 2140c0d06caSMauro Carvalho Chehab { 2150c0d06caSMauro Carvalho Chehab int err; 2160c0d06caSMauro Carvalho Chehab 2170c0d06caSMauro Carvalho Chehab /* Set red, green, blue, gain */ 2180c0d06caSMauro Carvalho Chehab err = stv06xx_write_bridge(sd, 0x0509, gain); 2190c0d06caSMauro Carvalho Chehab if (err < 0) 2200c0d06caSMauro Carvalho Chehab return err; 2210c0d06caSMauro Carvalho Chehab 2220c0d06caSMauro Carvalho Chehab err = stv06xx_write_bridge(sd, 0x050a, gain); 2230c0d06caSMauro Carvalho Chehab if (err < 0) 2240c0d06caSMauro Carvalho Chehab return err; 2250c0d06caSMauro Carvalho Chehab 2260c0d06caSMauro Carvalho Chehab err = stv06xx_write_bridge(sd, 0x050b, gain); 2270c0d06caSMauro Carvalho Chehab if (err < 0) 2280c0d06caSMauro Carvalho Chehab return err; 2290c0d06caSMauro Carvalho Chehab 2300c0d06caSMauro Carvalho Chehab /* 2 mystery writes */ 2310c0d06caSMauro Carvalho Chehab err = stv06xx_write_bridge(sd, 0x050c, 0x2a); 2320c0d06caSMauro Carvalho Chehab if (err < 0) 2330c0d06caSMauro Carvalho Chehab return err; 2340c0d06caSMauro Carvalho Chehab 2350c0d06caSMauro Carvalho Chehab return stv06xx_write_bridge(sd, 0x050d, 0x01); 2360c0d06caSMauro Carvalho Chehab } 2370c0d06caSMauro Carvalho Chehab 2380c0d06caSMauro Carvalho Chehab static int setexposure(struct sd *sd, s16 expo) 2390c0d06caSMauro Carvalho Chehab { 2400c0d06caSMauro Carvalho Chehab int err; 2410c0d06caSMauro Carvalho Chehab 2420c0d06caSMauro Carvalho Chehab err = stv06xx_write_bridge(sd, 0x143d, expo & 0xff); 2430c0d06caSMauro Carvalho Chehab if (err < 0) 2440c0d06caSMauro Carvalho Chehab return err; 2450c0d06caSMauro Carvalho Chehab 2460c0d06caSMauro Carvalho Chehab return stv06xx_write_bridge(sd, 0x143e, expo >> 8); 2470c0d06caSMauro Carvalho Chehab } 2480c0d06caSMauro Carvalho Chehab 2490c0d06caSMauro Carvalho Chehab static int st6422_start(struct sd *sd) 2500c0d06caSMauro Carvalho Chehab { 2510c0d06caSMauro Carvalho Chehab int err; 2520c0d06caSMauro Carvalho Chehab struct cam *cam = &sd->gspca_dev.cam; 2530c0d06caSMauro Carvalho Chehab 2540c0d06caSMauro Carvalho Chehab if (cam->cam_mode[sd->gspca_dev.curr_mode].priv) 2550c0d06caSMauro Carvalho Chehab err = stv06xx_write_bridge(sd, 0x1505, 0x0f); 2560c0d06caSMauro Carvalho Chehab else 2570c0d06caSMauro Carvalho Chehab err = stv06xx_write_bridge(sd, 0x1505, 0x02); 2580c0d06caSMauro Carvalho Chehab if (err < 0) 2590c0d06caSMauro Carvalho Chehab return err; 2600c0d06caSMauro Carvalho Chehab 2610c0d06caSMauro Carvalho Chehab /* commit settings */ 2620c0d06caSMauro Carvalho Chehab err = stv06xx_write_bridge(sd, 0x143f, 0x01); 2630c0d06caSMauro Carvalho Chehab return (err < 0) ? err : 0; 2640c0d06caSMauro Carvalho Chehab } 2650c0d06caSMauro Carvalho Chehab 2660c0d06caSMauro Carvalho Chehab static int st6422_stop(struct sd *sd) 2670c0d06caSMauro Carvalho Chehab { 268c93396e1STheodore Kilgore struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; 269c93396e1STheodore Kilgore 27037d5efb0SJoe Perches gspca_dbg(gspca_dev, D_STREAM, "Halting stream\n"); 2710c0d06caSMauro Carvalho Chehab 2720c0d06caSMauro Carvalho Chehab return 0; 2730c0d06caSMauro Carvalho Chehab } 274