18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for the po1030 sensor 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2008 Erik Andrén 68c2ecf20Sopenharmony_ci * Copyright (c) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. 78c2ecf20Sopenharmony_ci * Copyright (c) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Portions of code to USB interface and ALi driver software, 108c2ecf20Sopenharmony_ci * Copyright (c) 2006 Willem Duinker 118c2ecf20Sopenharmony_ci * v4l2 interface modeled after the V4L2 driver 128c2ecf20Sopenharmony_ci * for SN9C10x PC Camera Controllers 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "m5602_po1030.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic int po1030_s_ctrl(struct v4l2_ctrl *ctrl); 208c2ecf20Sopenharmony_cistatic void po1030_dump_registers(struct sd *sd); 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic const unsigned char preinit_po1030[][3] = { 238c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, 248c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, 258c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, 268c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, 278c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, 288c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, 298c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, 308c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, 318c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, 328c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, 338c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, 348c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, 358c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02}, 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci {SENSOR, PO1030_AUTOCTRL2, PO1030_SENSOR_RESET | (1 << 2)}, 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04}, 408c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, 418c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, 428c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, 438c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, 448c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, 458c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DAT, 0x00} 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic const unsigned char init_po1030[][3] = { 498c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, 508c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, 518c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, 528c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, 538c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, 548c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, 558c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci {SENSOR, PO1030_AUTOCTRL2, PO1030_SENSOR_RESET | (1 << 2)}, 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, 608c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, 618c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, 628c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, 638c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, 648c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02}, 658c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04}, 668c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, 678c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, 688c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci {SENSOR, PO1030_AUTOCTRL2, 0x04}, 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci {SENSOR, PO1030_OUTFORMCTRL2, PO1030_RAW_RGB_BAYER}, 738c2ecf20Sopenharmony_ci {SENSOR, PO1030_AUTOCTRL1, PO1030_WEIGHT_WIN_2X}, 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci {SENSOR, PO1030_CONTROL2, 0x03}, 768c2ecf20Sopenharmony_ci {SENSOR, 0x21, 0x90}, 778c2ecf20Sopenharmony_ci {SENSOR, PO1030_YTARGET, 0x60}, 788c2ecf20Sopenharmony_ci {SENSOR, 0x59, 0x13}, 798c2ecf20Sopenharmony_ci {SENSOR, PO1030_OUTFORMCTRL1, PO1030_HREF_ENABLE}, 808c2ecf20Sopenharmony_ci {SENSOR, PO1030_EDGE_ENH_OFF, 0x00}, 818c2ecf20Sopenharmony_ci {SENSOR, PO1030_EGA, 0x80}, 828c2ecf20Sopenharmony_ci {SENSOR, 0x78, 0x14}, 838c2ecf20Sopenharmony_ci {SENSOR, 0x6f, 0x01}, 848c2ecf20Sopenharmony_ci {SENSOR, PO1030_GLOBALGAINMAX, 0x14}, 858c2ecf20Sopenharmony_ci {SENSOR, PO1030_Cb_U_GAIN, 0x38}, 868c2ecf20Sopenharmony_ci {SENSOR, PO1030_Cr_V_GAIN, 0x38}, 878c2ecf20Sopenharmony_ci {SENSOR, PO1030_CONTROL1, PO1030_SHUTTER_MODE | 888c2ecf20Sopenharmony_ci PO1030_AUTO_SUBSAMPLING | 898c2ecf20Sopenharmony_ci PO1030_FRAME_EQUAL}, 908c2ecf20Sopenharmony_ci {SENSOR, PO1030_GC0, 0x10}, 918c2ecf20Sopenharmony_ci {SENSOR, PO1030_GC1, 0x20}, 928c2ecf20Sopenharmony_ci {SENSOR, PO1030_GC2, 0x40}, 938c2ecf20Sopenharmony_ci {SENSOR, PO1030_GC3, 0x60}, 948c2ecf20Sopenharmony_ci {SENSOR, PO1030_GC4, 0x80}, 958c2ecf20Sopenharmony_ci {SENSOR, PO1030_GC5, 0xa0}, 968c2ecf20Sopenharmony_ci {SENSOR, PO1030_GC6, 0xc0}, 978c2ecf20Sopenharmony_ci {SENSOR, PO1030_GC7, 0xff}, 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* Set the width to 751 */ 1008c2ecf20Sopenharmony_ci {SENSOR, PO1030_FRAMEWIDTH_H, 0x02}, 1018c2ecf20Sopenharmony_ci {SENSOR, PO1030_FRAMEWIDTH_L, 0xef}, 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* Set the height to 540 */ 1048c2ecf20Sopenharmony_ci {SENSOR, PO1030_FRAMEHEIGHT_H, 0x02}, 1058c2ecf20Sopenharmony_ci {SENSOR, PO1030_FRAMEHEIGHT_L, 0x1c}, 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* Set the x window to 1 */ 1088c2ecf20Sopenharmony_ci {SENSOR, PO1030_WINDOWX_H, 0x00}, 1098c2ecf20Sopenharmony_ci {SENSOR, PO1030_WINDOWX_L, 0x01}, 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* Set the y window to 1 */ 1128c2ecf20Sopenharmony_ci {SENSOR, PO1030_WINDOWY_H, 0x00}, 1138c2ecf20Sopenharmony_ci {SENSOR, PO1030_WINDOWY_L, 0x01}, 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* with a very low lighted environment increase the exposure but 1168c2ecf20Sopenharmony_ci * decrease the FPS (Frame Per Second) */ 1178c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, 1188c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, 1218c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, 1228c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, 1238c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, 1248c2ecf20Sopenharmony_ci}; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic struct v4l2_pix_format po1030_modes[] = { 1278c2ecf20Sopenharmony_ci { 1288c2ecf20Sopenharmony_ci 640, 1298c2ecf20Sopenharmony_ci 480, 1308c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SBGGR8, 1318c2ecf20Sopenharmony_ci V4L2_FIELD_NONE, 1328c2ecf20Sopenharmony_ci .sizeimage = 640 * 480, 1338c2ecf20Sopenharmony_ci .bytesperline = 640, 1348c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 1358c2ecf20Sopenharmony_ci .priv = 2 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci}; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops po1030_ctrl_ops = { 1408c2ecf20Sopenharmony_ci .s_ctrl = po1030_s_ctrl, 1418c2ecf20Sopenharmony_ci}; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_config po1030_greenbal_cfg = { 1448c2ecf20Sopenharmony_ci .ops = &po1030_ctrl_ops, 1458c2ecf20Sopenharmony_ci .id = M5602_V4L2_CID_GREEN_BALANCE, 1468c2ecf20Sopenharmony_ci .name = "Green Balance", 1478c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 1488c2ecf20Sopenharmony_ci .min = 0, 1498c2ecf20Sopenharmony_ci .max = 255, 1508c2ecf20Sopenharmony_ci .step = 1, 1518c2ecf20Sopenharmony_ci .def = PO1030_GREEN_GAIN_DEFAULT, 1528c2ecf20Sopenharmony_ci .flags = V4L2_CTRL_FLAG_SLIDER, 1538c2ecf20Sopenharmony_ci}; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ciint po1030_probe(struct sd *sd) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci u8 dev_id_h = 0, i; 1588c2ecf20Sopenharmony_ci int err; 1598c2ecf20Sopenharmony_ci struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (force_sensor) { 1628c2ecf20Sopenharmony_ci if (force_sensor == PO1030_SENSOR) { 1638c2ecf20Sopenharmony_ci pr_info("Forcing a %s sensor\n", po1030.name); 1648c2ecf20Sopenharmony_ci goto sensor_found; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci /* If we want to force another sensor, don't try to probe this 1678c2ecf20Sopenharmony_ci * one */ 1688c2ecf20Sopenharmony_ci return -ENODEV; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "Probing for a po1030 sensor\n"); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* Run the pre-init to actually probe the unit */ 1748c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(preinit_po1030); i++) { 1758c2ecf20Sopenharmony_ci u8 data = preinit_po1030[i][2]; 1768c2ecf20Sopenharmony_ci if (preinit_po1030[i][0] == SENSOR) 1778c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, preinit_po1030[i][1], 1788c2ecf20Sopenharmony_ci &data, 1); 1798c2ecf20Sopenharmony_ci else 1808c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, preinit_po1030[i][1], 1818c2ecf20Sopenharmony_ci data); 1828c2ecf20Sopenharmony_ci if (err < 0) 1838c2ecf20Sopenharmony_ci return err; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (m5602_read_sensor(sd, PO1030_DEVID_H, &dev_id_h, 1)) 1878c2ecf20Sopenharmony_ci return -ENODEV; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (dev_id_h == 0x30) { 1908c2ecf20Sopenharmony_ci pr_info("Detected a po1030 sensor\n"); 1918c2ecf20Sopenharmony_ci goto sensor_found; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci return -ENODEV; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cisensor_found: 1968c2ecf20Sopenharmony_ci sd->gspca_dev.cam.cam_mode = po1030_modes; 1978c2ecf20Sopenharmony_ci sd->gspca_dev.cam.nmodes = ARRAY_SIZE(po1030_modes); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci return 0; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ciint po1030_init(struct sd *sd) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci int i, err = 0; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* Init the sensor */ 2078c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(init_po1030) && !err; i++) { 2088c2ecf20Sopenharmony_ci u8 data[2] = {0x00, 0x00}; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci switch (init_po1030[i][0]) { 2118c2ecf20Sopenharmony_ci case BRIDGE: 2128c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, 2138c2ecf20Sopenharmony_ci init_po1030[i][1], 2148c2ecf20Sopenharmony_ci init_po1030[i][2]); 2158c2ecf20Sopenharmony_ci break; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci case SENSOR: 2188c2ecf20Sopenharmony_ci data[0] = init_po1030[i][2]; 2198c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, 2208c2ecf20Sopenharmony_ci init_po1030[i][1], data, 1); 2218c2ecf20Sopenharmony_ci break; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci default: 2248c2ecf20Sopenharmony_ci pr_info("Invalid stream command, exiting init\n"); 2258c2ecf20Sopenharmony_ci return -EINVAL; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci if (err < 0) 2298c2ecf20Sopenharmony_ci return err; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (dump_sensor) 2328c2ecf20Sopenharmony_ci po1030_dump_registers(sd); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci return 0; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ciint po1030_init_controls(struct sd *sd) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci sd->gspca_dev.vdev.ctrl_handler = hdl; 2428c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 9); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci sd->auto_white_bal = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops, 2458c2ecf20Sopenharmony_ci V4L2_CID_AUTO_WHITE_BALANCE, 2468c2ecf20Sopenharmony_ci 0, 1, 1, 0); 2478c2ecf20Sopenharmony_ci sd->green_bal = v4l2_ctrl_new_custom(hdl, &po1030_greenbal_cfg, NULL); 2488c2ecf20Sopenharmony_ci sd->red_bal = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops, 2498c2ecf20Sopenharmony_ci V4L2_CID_RED_BALANCE, 0, 255, 1, 2508c2ecf20Sopenharmony_ci PO1030_RED_GAIN_DEFAULT); 2518c2ecf20Sopenharmony_ci sd->blue_bal = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops, 2528c2ecf20Sopenharmony_ci V4L2_CID_BLUE_BALANCE, 0, 255, 1, 2538c2ecf20Sopenharmony_ci PO1030_BLUE_GAIN_DEFAULT); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci sd->autoexpo = v4l2_ctrl_new_std_menu(hdl, &po1030_ctrl_ops, 2568c2ecf20Sopenharmony_ci V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_MANUAL); 2578c2ecf20Sopenharmony_ci sd->expo = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops, V4L2_CID_EXPOSURE, 2588c2ecf20Sopenharmony_ci 0, 0x2ff, 1, PO1030_EXPOSURE_DEFAULT); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci sd->gain = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops, V4L2_CID_GAIN, 0, 2618c2ecf20Sopenharmony_ci 0x4f, 1, PO1030_GLOBAL_GAIN_DEFAULT); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci sd->hflip = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops, V4L2_CID_HFLIP, 2648c2ecf20Sopenharmony_ci 0, 1, 1, 0); 2658c2ecf20Sopenharmony_ci sd->vflip = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops, V4L2_CID_VFLIP, 2668c2ecf20Sopenharmony_ci 0, 1, 1, 0); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (hdl->error) { 2698c2ecf20Sopenharmony_ci pr_err("Could not initialize controls\n"); 2708c2ecf20Sopenharmony_ci return hdl->error; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci v4l2_ctrl_auto_cluster(4, &sd->auto_white_bal, 0, false); 2748c2ecf20Sopenharmony_ci v4l2_ctrl_auto_cluster(2, &sd->autoexpo, 0, false); 2758c2ecf20Sopenharmony_ci v4l2_ctrl_cluster(2, &sd->hflip); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci return 0; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ciint po1030_start(struct sd *sd) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci struct cam *cam = &sd->gspca_dev.cam; 2838c2ecf20Sopenharmony_ci int i, err = 0; 2848c2ecf20Sopenharmony_ci int width = cam->cam_mode[sd->gspca_dev.curr_mode].width; 2858c2ecf20Sopenharmony_ci int height = cam->cam_mode[sd->gspca_dev.curr_mode].height; 2868c2ecf20Sopenharmony_ci int ver_offs = cam->cam_mode[sd->gspca_dev.curr_mode].priv; 2878c2ecf20Sopenharmony_ci u8 data; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci switch (width) { 2908c2ecf20Sopenharmony_ci case 320: 2918c2ecf20Sopenharmony_ci data = PO1030_SUBSAMPLING; 2928c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, PO1030_CONTROL3, &data, 1); 2938c2ecf20Sopenharmony_ci if (err < 0) 2948c2ecf20Sopenharmony_ci return err; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci data = ((width + 3) >> 8) & 0xff; 2978c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_H, &data, 1); 2988c2ecf20Sopenharmony_ci if (err < 0) 2998c2ecf20Sopenharmony_ci return err; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci data = (width + 3) & 0xff; 3028c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_L, &data, 1); 3038c2ecf20Sopenharmony_ci if (err < 0) 3048c2ecf20Sopenharmony_ci return err; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci data = ((height + 1) >> 8) & 0xff; 3078c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_H, &data, 1); 3088c2ecf20Sopenharmony_ci if (err < 0) 3098c2ecf20Sopenharmony_ci return err; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci data = (height + 1) & 0xff; 3128c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_L, &data, 1); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci height += 6; 3158c2ecf20Sopenharmony_ci width -= 1; 3168c2ecf20Sopenharmony_ci break; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci case 640: 3198c2ecf20Sopenharmony_ci data = 0; 3208c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, PO1030_CONTROL3, &data, 1); 3218c2ecf20Sopenharmony_ci if (err < 0) 3228c2ecf20Sopenharmony_ci return err; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci data = ((width + 7) >> 8) & 0xff; 3258c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_H, &data, 1); 3268c2ecf20Sopenharmony_ci if (err < 0) 3278c2ecf20Sopenharmony_ci return err; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci data = (width + 7) & 0xff; 3308c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_L, &data, 1); 3318c2ecf20Sopenharmony_ci if (err < 0) 3328c2ecf20Sopenharmony_ci return err; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci data = ((height + 3) >> 8) & 0xff; 3358c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_H, &data, 1); 3368c2ecf20Sopenharmony_ci if (err < 0) 3378c2ecf20Sopenharmony_ci return err; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci data = (height + 3) & 0xff; 3408c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_L, &data, 1); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci height += 12; 3438c2ecf20Sopenharmony_ci width -= 2; 3448c2ecf20Sopenharmony_ci break; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_SENSOR_TYPE, 0x0c); 3478c2ecf20Sopenharmony_ci if (err < 0) 3488c2ecf20Sopenharmony_ci return err; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_LINE_OF_FRAME_H, 0x81); 3518c2ecf20Sopenharmony_ci if (err < 0) 3528c2ecf20Sopenharmony_ci return err; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_PIX_OF_LINE_H, 0x82); 3558c2ecf20Sopenharmony_ci if (err < 0) 3568c2ecf20Sopenharmony_ci return err; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0x01); 3598c2ecf20Sopenharmony_ci if (err < 0) 3608c2ecf20Sopenharmony_ci return err; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 3638c2ecf20Sopenharmony_ci ((ver_offs >> 8) & 0xff)); 3648c2ecf20Sopenharmony_ci if (err < 0) 3658c2ecf20Sopenharmony_ci return err; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (ver_offs & 0xff)); 3688c2ecf20Sopenharmony_ci if (err < 0) 3698c2ecf20Sopenharmony_ci return err; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci for (i = 0; i < 2 && !err; i++) 3728c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0); 3738c2ecf20Sopenharmony_ci if (err < 0) 3748c2ecf20Sopenharmony_ci return err; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff); 3778c2ecf20Sopenharmony_ci if (err < 0) 3788c2ecf20Sopenharmony_ci return err; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff)); 3818c2ecf20Sopenharmony_ci if (err < 0) 3828c2ecf20Sopenharmony_ci return err; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci for (i = 0; i < 2 && !err; i++) 3858c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci for (i = 0; i < 2 && !err; i++) 3888c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci for (i = 0; i < 2 && !err; i++) 3918c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 0); 3928c2ecf20Sopenharmony_ci if (err < 0) 3938c2ecf20Sopenharmony_ci return err; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, (width >> 8) & 0xff); 3968c2ecf20Sopenharmony_ci if (err < 0) 3978c2ecf20Sopenharmony_ci return err; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, (width & 0xff)); 4008c2ecf20Sopenharmony_ci if (err < 0) 4018c2ecf20Sopenharmony_ci return err; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); 4048c2ecf20Sopenharmony_ci return err; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 4108c2ecf20Sopenharmony_ci u8 i2c_data; 4118c2ecf20Sopenharmony_ci int err; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set exposure to %d\n", val & 0xffff); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci i2c_data = ((val & 0xff00) >> 8); 4168c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set exposure to high byte to 0x%x\n", 4178c2ecf20Sopenharmony_ci i2c_data); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, PO1030_INTEGLINES_H, 4208c2ecf20Sopenharmony_ci &i2c_data, 1); 4218c2ecf20Sopenharmony_ci if (err < 0) 4228c2ecf20Sopenharmony_ci return err; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci i2c_data = (val & 0xff); 4258c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set exposure to low byte to 0x%x\n", 4268c2ecf20Sopenharmony_ci i2c_data); 4278c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, PO1030_INTEGLINES_M, 4288c2ecf20Sopenharmony_ci &i2c_data, 1); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci return err; 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 4368c2ecf20Sopenharmony_ci u8 i2c_data; 4378c2ecf20Sopenharmony_ci int err; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci i2c_data = val & 0xff; 4408c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set global gain to %d\n", i2c_data); 4418c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, PO1030_GLOBALGAIN, 4428c2ecf20Sopenharmony_ci &i2c_data, 1); 4438c2ecf20Sopenharmony_ci return err; 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic int po1030_set_hvflip(struct gspca_dev *gspca_dev) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 4498c2ecf20Sopenharmony_ci u8 i2c_data; 4508c2ecf20Sopenharmony_ci int err; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set hvflip %d %d\n", 4538c2ecf20Sopenharmony_ci sd->hflip->val, sd->vflip->val); 4548c2ecf20Sopenharmony_ci err = m5602_read_sensor(sd, PO1030_CONTROL2, &i2c_data, 1); 4558c2ecf20Sopenharmony_ci if (err < 0) 4568c2ecf20Sopenharmony_ci return err; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci i2c_data = (0x3f & i2c_data) | (sd->hflip->val << 7) | 4598c2ecf20Sopenharmony_ci (sd->vflip->val << 6); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, PO1030_CONTROL2, 4628c2ecf20Sopenharmony_ci &i2c_data, 1); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci return err; 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 4708c2ecf20Sopenharmony_ci u8 i2c_data; 4718c2ecf20Sopenharmony_ci int err; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci i2c_data = val & 0xff; 4748c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set red gain to %d\n", i2c_data); 4758c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, PO1030_RED_GAIN, 4768c2ecf20Sopenharmony_ci &i2c_data, 1); 4778c2ecf20Sopenharmony_ci return err; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 4838c2ecf20Sopenharmony_ci u8 i2c_data; 4848c2ecf20Sopenharmony_ci int err; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci i2c_data = val & 0xff; 4878c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set blue gain to %d\n", i2c_data); 4888c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, PO1030_BLUE_GAIN, 4898c2ecf20Sopenharmony_ci &i2c_data, 1); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci return err; 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic int po1030_set_green_balance(struct gspca_dev *gspca_dev, __s32 val) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 4978c2ecf20Sopenharmony_ci u8 i2c_data; 4988c2ecf20Sopenharmony_ci int err; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci i2c_data = val & 0xff; 5018c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set green gain to %d\n", i2c_data); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, PO1030_GREEN_1_GAIN, 5048c2ecf20Sopenharmony_ci &i2c_data, 1); 5058c2ecf20Sopenharmony_ci if (err < 0) 5068c2ecf20Sopenharmony_ci return err; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci return m5602_write_sensor(sd, PO1030_GREEN_2_GAIN, 5098c2ecf20Sopenharmony_ci &i2c_data, 1); 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_cistatic int po1030_set_auto_white_balance(struct gspca_dev *gspca_dev, 5138c2ecf20Sopenharmony_ci __s32 val) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 5168c2ecf20Sopenharmony_ci u8 i2c_data; 5178c2ecf20Sopenharmony_ci int err; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci err = m5602_read_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1); 5208c2ecf20Sopenharmony_ci if (err < 0) 5218c2ecf20Sopenharmony_ci return err; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set auto white balance to %d\n", val); 5248c2ecf20Sopenharmony_ci i2c_data = (i2c_data & 0xfe) | (val & 0x01); 5258c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1); 5268c2ecf20Sopenharmony_ci return err; 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic int po1030_set_auto_exposure(struct gspca_dev *gspca_dev, 5308c2ecf20Sopenharmony_ci __s32 val) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 5338c2ecf20Sopenharmony_ci u8 i2c_data; 5348c2ecf20Sopenharmony_ci int err; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci err = m5602_read_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1); 5378c2ecf20Sopenharmony_ci if (err < 0) 5388c2ecf20Sopenharmony_ci return err; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set auto exposure to %d\n", val); 5418c2ecf20Sopenharmony_ci val = (val == V4L2_EXPOSURE_AUTO); 5428c2ecf20Sopenharmony_ci i2c_data = (i2c_data & 0xfd) | ((val & 0x01) << 1); 5438c2ecf20Sopenharmony_ci return m5602_write_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1); 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_civoid po1030_disconnect(struct sd *sd) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci sd->sensor = NULL; 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cistatic int po1030_s_ctrl(struct v4l2_ctrl *ctrl) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci struct gspca_dev *gspca_dev = 5548c2ecf20Sopenharmony_ci container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 5558c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 5568c2ecf20Sopenharmony_ci int err; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (!gspca_dev->streaming) 5598c2ecf20Sopenharmony_ci return 0; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci switch (ctrl->id) { 5628c2ecf20Sopenharmony_ci case V4L2_CID_AUTO_WHITE_BALANCE: 5638c2ecf20Sopenharmony_ci err = po1030_set_auto_white_balance(gspca_dev, ctrl->val); 5648c2ecf20Sopenharmony_ci if (err || ctrl->val) 5658c2ecf20Sopenharmony_ci return err; 5668c2ecf20Sopenharmony_ci err = po1030_set_green_balance(gspca_dev, sd->green_bal->val); 5678c2ecf20Sopenharmony_ci if (err) 5688c2ecf20Sopenharmony_ci return err; 5698c2ecf20Sopenharmony_ci err = po1030_set_red_balance(gspca_dev, sd->red_bal->val); 5708c2ecf20Sopenharmony_ci if (err) 5718c2ecf20Sopenharmony_ci return err; 5728c2ecf20Sopenharmony_ci err = po1030_set_blue_balance(gspca_dev, sd->blue_bal->val); 5738c2ecf20Sopenharmony_ci break; 5748c2ecf20Sopenharmony_ci case V4L2_CID_EXPOSURE_AUTO: 5758c2ecf20Sopenharmony_ci err = po1030_set_auto_exposure(gspca_dev, ctrl->val); 5768c2ecf20Sopenharmony_ci if (err || ctrl->val == V4L2_EXPOSURE_AUTO) 5778c2ecf20Sopenharmony_ci return err; 5788c2ecf20Sopenharmony_ci err = po1030_set_exposure(gspca_dev, sd->expo->val); 5798c2ecf20Sopenharmony_ci break; 5808c2ecf20Sopenharmony_ci case V4L2_CID_GAIN: 5818c2ecf20Sopenharmony_ci err = po1030_set_gain(gspca_dev, ctrl->val); 5828c2ecf20Sopenharmony_ci break; 5838c2ecf20Sopenharmony_ci case V4L2_CID_HFLIP: 5848c2ecf20Sopenharmony_ci err = po1030_set_hvflip(gspca_dev); 5858c2ecf20Sopenharmony_ci break; 5868c2ecf20Sopenharmony_ci default: 5878c2ecf20Sopenharmony_ci return -EINVAL; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci return err; 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic void po1030_dump_registers(struct sd *sd) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci int address; 5968c2ecf20Sopenharmony_ci u8 value = 0; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci pr_info("Dumping the po1030 sensor core registers\n"); 5998c2ecf20Sopenharmony_ci for (address = 0; address < 0x7f; address++) { 6008c2ecf20Sopenharmony_ci m5602_read_sensor(sd, address, &value, 1); 6018c2ecf20Sopenharmony_ci pr_info("register 0x%x contains 0x%x\n", address, value); 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci pr_info("po1030 register state dump complete\n"); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci pr_info("Probing for which registers that are read/write\n"); 6078c2ecf20Sopenharmony_ci for (address = 0; address < 0xff; address++) { 6088c2ecf20Sopenharmony_ci u8 old_value, ctrl_value; 6098c2ecf20Sopenharmony_ci u8 test_value[2] = {0xff, 0xff}; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci m5602_read_sensor(sd, address, &old_value, 1); 6128c2ecf20Sopenharmony_ci m5602_write_sensor(sd, address, test_value, 1); 6138c2ecf20Sopenharmony_ci m5602_read_sensor(sd, address, &ctrl_value, 1); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (ctrl_value == test_value[0]) 6168c2ecf20Sopenharmony_ci pr_info("register 0x%x is writeable\n", address); 6178c2ecf20Sopenharmony_ci else 6188c2ecf20Sopenharmony_ci pr_info("register 0x%x is read only\n", address); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci /* Restore original value */ 6218c2ecf20Sopenharmony_ci m5602_write_sensor(sd, address, &old_value, 1); 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci} 624