18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * GS1662 device registration. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015-2016 Nexvision 68c2ecf20Sopenharmony_ci * Author: Charles-Antoine Couret <charles-antoine.couret@nexvision.fr> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/ctype.h> 148c2ecf20Sopenharmony_ci#include <linux/err.h> 158c2ecf20Sopenharmony_ci#include <linux/device.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 198c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 208c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 218c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 228c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h> 238c2ecf20Sopenharmony_ci#include <media/v4l2-dv-timings.h> 248c2ecf20Sopenharmony_ci#include <linux/v4l2-dv-timings.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define REG_STATUS 0x04 278c2ecf20Sopenharmony_ci#define REG_FORCE_FMT 0x06 288c2ecf20Sopenharmony_ci#define REG_LINES_PER_FRAME 0x12 298c2ecf20Sopenharmony_ci#define REG_WORDS_PER_LINE 0x13 308c2ecf20Sopenharmony_ci#define REG_WORDS_PER_ACT_LINE 0x14 318c2ecf20Sopenharmony_ci#define REG_ACT_LINES_PER_FRAME 0x15 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define MASK_H_LOCK 0x001 348c2ecf20Sopenharmony_ci#define MASK_V_LOCK 0x002 358c2ecf20Sopenharmony_ci#define MASK_STD_LOCK 0x004 368c2ecf20Sopenharmony_ci#define MASK_FORCE_STD 0x020 378c2ecf20Sopenharmony_ci#define MASK_STD_STATUS 0x3E0 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define GS_WIDTH_MIN 720 408c2ecf20Sopenharmony_ci#define GS_WIDTH_MAX 2048 418c2ecf20Sopenharmony_ci#define GS_HEIGHT_MIN 487 428c2ecf20Sopenharmony_ci#define GS_HEIGHT_MAX 1080 438c2ecf20Sopenharmony_ci#define GS_PIXELCLOCK_MIN 10519200 448c2ecf20Sopenharmony_ci#define GS_PIXELCLOCK_MAX 74250000 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct gs { 478c2ecf20Sopenharmony_ci struct spi_device *pdev; 488c2ecf20Sopenharmony_ci struct v4l2_subdev sd; 498c2ecf20Sopenharmony_ci struct v4l2_dv_timings current_timings; 508c2ecf20Sopenharmony_ci int enabled; 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistruct gs_reg_fmt { 548c2ecf20Sopenharmony_ci u16 reg_value; 558c2ecf20Sopenharmony_ci struct v4l2_dv_timings format; 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistruct gs_reg_fmt_custom { 598c2ecf20Sopenharmony_ci u16 reg_value; 608c2ecf20Sopenharmony_ci __u32 width; 618c2ecf20Sopenharmony_ci __u32 height; 628c2ecf20Sopenharmony_ci __u64 pixelclock; 638c2ecf20Sopenharmony_ci __u32 interlaced; 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic const struct spi_device_id gs_id[] = { 678c2ecf20Sopenharmony_ci { "gs1662", 0 }, 688c2ecf20Sopenharmony_ci { } 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(spi, gs_id); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic const struct v4l2_dv_timings fmt_cap[] = { 738c2ecf20Sopenharmony_ci V4L2_DV_BT_SDI_720X487I60, 748c2ecf20Sopenharmony_ci V4L2_DV_BT_CEA_720X576P50, 758c2ecf20Sopenharmony_ci V4L2_DV_BT_CEA_1280X720P24, 768c2ecf20Sopenharmony_ci V4L2_DV_BT_CEA_1280X720P25, 778c2ecf20Sopenharmony_ci V4L2_DV_BT_CEA_1280X720P30, 788c2ecf20Sopenharmony_ci V4L2_DV_BT_CEA_1280X720P50, 798c2ecf20Sopenharmony_ci V4L2_DV_BT_CEA_1280X720P60, 808c2ecf20Sopenharmony_ci V4L2_DV_BT_CEA_1920X1080P24, 818c2ecf20Sopenharmony_ci V4L2_DV_BT_CEA_1920X1080P25, 828c2ecf20Sopenharmony_ci V4L2_DV_BT_CEA_1920X1080P30, 838c2ecf20Sopenharmony_ci V4L2_DV_BT_CEA_1920X1080I50, 848c2ecf20Sopenharmony_ci V4L2_DV_BT_CEA_1920X1080I60, 858c2ecf20Sopenharmony_ci}; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic const struct gs_reg_fmt reg_fmt[] = { 888c2ecf20Sopenharmony_ci { 0x00, V4L2_DV_BT_CEA_1280X720P60 }, 898c2ecf20Sopenharmony_ci { 0x01, V4L2_DV_BT_CEA_1280X720P60 }, 908c2ecf20Sopenharmony_ci { 0x02, V4L2_DV_BT_CEA_1280X720P30 }, 918c2ecf20Sopenharmony_ci { 0x03, V4L2_DV_BT_CEA_1280X720P30 }, 928c2ecf20Sopenharmony_ci { 0x04, V4L2_DV_BT_CEA_1280X720P50 }, 938c2ecf20Sopenharmony_ci { 0x05, V4L2_DV_BT_CEA_1280X720P50 }, 948c2ecf20Sopenharmony_ci { 0x06, V4L2_DV_BT_CEA_1280X720P25 }, 958c2ecf20Sopenharmony_ci { 0x07, V4L2_DV_BT_CEA_1280X720P25 }, 968c2ecf20Sopenharmony_ci { 0x08, V4L2_DV_BT_CEA_1280X720P24 }, 978c2ecf20Sopenharmony_ci { 0x09, V4L2_DV_BT_CEA_1280X720P24 }, 988c2ecf20Sopenharmony_ci { 0x0A, V4L2_DV_BT_CEA_1920X1080I60 }, 998c2ecf20Sopenharmony_ci { 0x0B, V4L2_DV_BT_CEA_1920X1080P30 }, 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* Default value: keep this field before 0xC */ 1028c2ecf20Sopenharmony_ci { 0x14, V4L2_DV_BT_CEA_1920X1080I50 }, 1038c2ecf20Sopenharmony_ci { 0x0C, V4L2_DV_BT_CEA_1920X1080I50 }, 1048c2ecf20Sopenharmony_ci { 0x0D, V4L2_DV_BT_CEA_1920X1080P25 }, 1058c2ecf20Sopenharmony_ci { 0x0E, V4L2_DV_BT_CEA_1920X1080P25 }, 1068c2ecf20Sopenharmony_ci { 0x10, V4L2_DV_BT_CEA_1920X1080P24 }, 1078c2ecf20Sopenharmony_ci { 0x12, V4L2_DV_BT_CEA_1920X1080P24 }, 1088c2ecf20Sopenharmony_ci { 0x16, V4L2_DV_BT_SDI_720X487I60 }, 1098c2ecf20Sopenharmony_ci { 0x19, V4L2_DV_BT_SDI_720X487I60 }, 1108c2ecf20Sopenharmony_ci { 0x18, V4L2_DV_BT_CEA_720X576P50 }, 1118c2ecf20Sopenharmony_ci { 0x1A, V4L2_DV_BT_CEA_720X576P50 }, 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* Implement following timings before enable it. 1148c2ecf20Sopenharmony_ci * Because of we don't have access to these theoretical timings yet. 1158c2ecf20Sopenharmony_ci * Workaround: use functions to get and set registers for these formats. 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_ci#if 0 1188c2ecf20Sopenharmony_ci { 0x0F, V4L2_DV_BT_XXX_1920X1080I25 }, /* SMPTE 274M */ 1198c2ecf20Sopenharmony_ci { 0x11, V4L2_DV_BT_XXX_1920X1080I24 }, /* SMPTE 274M */ 1208c2ecf20Sopenharmony_ci { 0x13, V4L2_DV_BT_XXX_1920X1080I25 }, /* SMPTE 274M */ 1218c2ecf20Sopenharmony_ci { 0x15, V4L2_DV_BT_XXX_1920X1035I60 }, /* SMPTE 260M */ 1228c2ecf20Sopenharmony_ci { 0x17, V4L2_DV_BT_SDI_720X507I60 }, /* SMPTE 125M */ 1238c2ecf20Sopenharmony_ci { 0x1B, V4L2_DV_BT_SDI_720X507I60 }, /* SMPTE 125M */ 1248c2ecf20Sopenharmony_ci { 0x1C, V4L2_DV_BT_XXX_2048X1080P25 }, /* SMPTE 428.1M */ 1258c2ecf20Sopenharmony_ci#endif 1268c2ecf20Sopenharmony_ci}; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic const struct v4l2_dv_timings_cap gs_timings_cap = { 1298c2ecf20Sopenharmony_ci .type = V4L2_DV_BT_656_1120, 1308c2ecf20Sopenharmony_ci /* keep this initialization for compatibility with GCC < 4.4.6 */ 1318c2ecf20Sopenharmony_ci .reserved = { 0 }, 1328c2ecf20Sopenharmony_ci V4L2_INIT_BT_TIMINGS(GS_WIDTH_MIN, GS_WIDTH_MAX, GS_HEIGHT_MIN, 1338c2ecf20Sopenharmony_ci GS_HEIGHT_MAX, GS_PIXELCLOCK_MIN, 1348c2ecf20Sopenharmony_ci GS_PIXELCLOCK_MAX, 1358c2ecf20Sopenharmony_ci V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_SDI, 1368c2ecf20Sopenharmony_ci V4L2_DV_BT_CAP_PROGRESSIVE 1378c2ecf20Sopenharmony_ci | V4L2_DV_BT_CAP_INTERLACED) 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int gs_read_register(struct spi_device *spi, u16 addr, u16 *value) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci int ret; 1438c2ecf20Sopenharmony_ci u16 buf_addr = (0x8000 | (0x0FFF & addr)); 1448c2ecf20Sopenharmony_ci u16 buf_value = 0; 1458c2ecf20Sopenharmony_ci struct spi_message msg; 1468c2ecf20Sopenharmony_ci struct spi_transfer tx[] = { 1478c2ecf20Sopenharmony_ci { 1488c2ecf20Sopenharmony_ci .tx_buf = &buf_addr, 1498c2ecf20Sopenharmony_ci .len = 2, 1508c2ecf20Sopenharmony_ci .delay = { 1518c2ecf20Sopenharmony_ci .value = 1, 1528c2ecf20Sopenharmony_ci .unit = SPI_DELAY_UNIT_USECS 1538c2ecf20Sopenharmony_ci }, 1548c2ecf20Sopenharmony_ci }, { 1558c2ecf20Sopenharmony_ci .rx_buf = &buf_value, 1568c2ecf20Sopenharmony_ci .len = 2, 1578c2ecf20Sopenharmony_ci .delay = { 1588c2ecf20Sopenharmony_ci .value = 1, 1598c2ecf20Sopenharmony_ci .unit = SPI_DELAY_UNIT_USECS 1608c2ecf20Sopenharmony_ci }, 1618c2ecf20Sopenharmony_ci }, 1628c2ecf20Sopenharmony_ci }; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci spi_message_init(&msg); 1658c2ecf20Sopenharmony_ci spi_message_add_tail(&tx[0], &msg); 1668c2ecf20Sopenharmony_ci spi_message_add_tail(&tx[1], &msg); 1678c2ecf20Sopenharmony_ci ret = spi_sync(spi, &msg); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci *value = buf_value; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return ret; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int gs_write_register(struct spi_device *spi, u16 addr, u16 value) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci int ret; 1778c2ecf20Sopenharmony_ci u16 buf_addr = addr; 1788c2ecf20Sopenharmony_ci u16 buf_value = value; 1798c2ecf20Sopenharmony_ci struct spi_message msg; 1808c2ecf20Sopenharmony_ci struct spi_transfer tx[] = { 1818c2ecf20Sopenharmony_ci { 1828c2ecf20Sopenharmony_ci .tx_buf = &buf_addr, 1838c2ecf20Sopenharmony_ci .len = 2, 1848c2ecf20Sopenharmony_ci .delay = { 1858c2ecf20Sopenharmony_ci .value = 1, 1868c2ecf20Sopenharmony_ci .unit = SPI_DELAY_UNIT_USECS 1878c2ecf20Sopenharmony_ci }, 1888c2ecf20Sopenharmony_ci }, { 1898c2ecf20Sopenharmony_ci .tx_buf = &buf_value, 1908c2ecf20Sopenharmony_ci .len = 2, 1918c2ecf20Sopenharmony_ci .delay = { 1928c2ecf20Sopenharmony_ci .value = 1, 1938c2ecf20Sopenharmony_ci .unit = SPI_DELAY_UNIT_USECS 1948c2ecf20Sopenharmony_ci }, 1958c2ecf20Sopenharmony_ci }, 1968c2ecf20Sopenharmony_ci }; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci spi_message_init(&msg); 1998c2ecf20Sopenharmony_ci spi_message_add_tail(&tx[0], &msg); 2008c2ecf20Sopenharmony_ci spi_message_add_tail(&tx[1], &msg); 2018c2ecf20Sopenharmony_ci ret = spi_sync(spi, &msg); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci return ret; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 2078c2ecf20Sopenharmony_cistatic int gs_g_register(struct v4l2_subdev *sd, 2088c2ecf20Sopenharmony_ci struct v4l2_dbg_register *reg) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct spi_device *spi = v4l2_get_subdevdata(sd); 2118c2ecf20Sopenharmony_ci u16 val; 2128c2ecf20Sopenharmony_ci int ret; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci ret = gs_read_register(spi, reg->reg & 0xFFFF, &val); 2158c2ecf20Sopenharmony_ci reg->val = val; 2168c2ecf20Sopenharmony_ci reg->size = 2; 2178c2ecf20Sopenharmony_ci return ret; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic int gs_s_register(struct v4l2_subdev *sd, 2218c2ecf20Sopenharmony_ci const struct v4l2_dbg_register *reg) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct spi_device *spi = v4l2_get_subdevdata(sd); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return gs_write_register(spi, reg->reg & 0xFFFF, reg->val & 0xFFFF); 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci#endif 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int gs_status_format(u16 status, struct v4l2_dv_timings *timings) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci int std = (status & MASK_STD_STATUS) >> 5; 2328c2ecf20Sopenharmony_ci int i; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(reg_fmt); i++) { 2358c2ecf20Sopenharmony_ci if (reg_fmt[i].reg_value == std) { 2368c2ecf20Sopenharmony_ci *timings = reg_fmt[i].format; 2378c2ecf20Sopenharmony_ci return 0; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return -ERANGE; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic u16 get_register_timings(struct v4l2_dv_timings *timings) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci int i; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(reg_fmt); i++) { 2498c2ecf20Sopenharmony_ci if (v4l2_match_dv_timings(timings, ®_fmt[i].format, 0, 2508c2ecf20Sopenharmony_ci false)) 2518c2ecf20Sopenharmony_ci return reg_fmt[i].reg_value | MASK_FORCE_STD; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return 0x0; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic inline struct gs *to_gs(struct v4l2_subdev *sd) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci return container_of(sd, struct gs, sd); 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic int gs_s_dv_timings(struct v4l2_subdev *sd, 2638c2ecf20Sopenharmony_ci struct v4l2_dv_timings *timings) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci struct gs *gs = to_gs(sd); 2668c2ecf20Sopenharmony_ci int reg_value; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci reg_value = get_register_timings(timings); 2698c2ecf20Sopenharmony_ci if (reg_value == 0x0) 2708c2ecf20Sopenharmony_ci return -EINVAL; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci gs->current_timings = *timings; 2738c2ecf20Sopenharmony_ci return 0; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic int gs_g_dv_timings(struct v4l2_subdev *sd, 2778c2ecf20Sopenharmony_ci struct v4l2_dv_timings *timings) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct gs *gs = to_gs(sd); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci *timings = gs->current_timings; 2828c2ecf20Sopenharmony_ci return 0; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic int gs_query_dv_timings(struct v4l2_subdev *sd, 2868c2ecf20Sopenharmony_ci struct v4l2_dv_timings *timings) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct gs *gs = to_gs(sd); 2898c2ecf20Sopenharmony_ci struct v4l2_dv_timings fmt; 2908c2ecf20Sopenharmony_ci u16 reg_value, i; 2918c2ecf20Sopenharmony_ci int ret; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (gs->enabled) 2948c2ecf20Sopenharmony_ci return -EBUSY; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* 2978c2ecf20Sopenharmony_ci * Check if the component detect a line, a frame or something else 2988c2ecf20Sopenharmony_ci * which looks like a video signal activity. 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 3018c2ecf20Sopenharmony_ci gs_read_register(gs->pdev, REG_LINES_PER_FRAME + i, ®_value); 3028c2ecf20Sopenharmony_ci if (reg_value) 3038c2ecf20Sopenharmony_ci break; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* If no register reports a video signal */ 3078c2ecf20Sopenharmony_ci if (i >= 4) 3088c2ecf20Sopenharmony_ci return -ENOLINK; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci gs_read_register(gs->pdev, REG_STATUS, ®_value); 3118c2ecf20Sopenharmony_ci if (!(reg_value & MASK_H_LOCK) || !(reg_value & MASK_V_LOCK)) 3128c2ecf20Sopenharmony_ci return -ENOLCK; 3138c2ecf20Sopenharmony_ci if (!(reg_value & MASK_STD_LOCK)) 3148c2ecf20Sopenharmony_ci return -ERANGE; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci ret = gs_status_format(reg_value, &fmt); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (ret < 0) 3198c2ecf20Sopenharmony_ci return ret; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci *timings = fmt; 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic int gs_enum_dv_timings(struct v4l2_subdev *sd, 3268c2ecf20Sopenharmony_ci struct v4l2_enum_dv_timings *timings) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci if (timings->index >= ARRAY_SIZE(fmt_cap)) 3298c2ecf20Sopenharmony_ci return -EINVAL; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (timings->pad != 0) 3328c2ecf20Sopenharmony_ci return -EINVAL; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci timings->timings = fmt_cap[timings->index]; 3358c2ecf20Sopenharmony_ci return 0; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int gs_s_stream(struct v4l2_subdev *sd, int enable) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct gs *gs = to_gs(sd); 3418c2ecf20Sopenharmony_ci int reg_value; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (gs->enabled == enable) 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci gs->enabled = enable; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (enable) { 3498c2ecf20Sopenharmony_ci /* To force the specific format */ 3508c2ecf20Sopenharmony_ci reg_value = get_register_timings(&gs->current_timings); 3518c2ecf20Sopenharmony_ci return gs_write_register(gs->pdev, REG_FORCE_FMT, reg_value); 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* To renable auto-detection mode */ 3558c2ecf20Sopenharmony_ci return gs_write_register(gs->pdev, REG_FORCE_FMT, 0x0); 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic int gs_g_input_status(struct v4l2_subdev *sd, u32 *status) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct gs *gs = to_gs(sd); 3618c2ecf20Sopenharmony_ci u16 reg_value, i; 3628c2ecf20Sopenharmony_ci int ret; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* 3658c2ecf20Sopenharmony_ci * Check if the component detect a line, a frame or something else 3668c2ecf20Sopenharmony_ci * which looks like a video signal activity. 3678c2ecf20Sopenharmony_ci */ 3688c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 3698c2ecf20Sopenharmony_ci ret = gs_read_register(gs->pdev, 3708c2ecf20Sopenharmony_ci REG_LINES_PER_FRAME + i, ®_value); 3718c2ecf20Sopenharmony_ci if (reg_value) 3728c2ecf20Sopenharmony_ci break; 3738c2ecf20Sopenharmony_ci if (ret) { 3748c2ecf20Sopenharmony_ci *status = V4L2_IN_ST_NO_POWER; 3758c2ecf20Sopenharmony_ci return ret; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* If no register reports a video signal */ 3808c2ecf20Sopenharmony_ci if (i >= 4) 3818c2ecf20Sopenharmony_ci *status |= V4L2_IN_ST_NO_SIGNAL; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci ret = gs_read_register(gs->pdev, REG_STATUS, ®_value); 3848c2ecf20Sopenharmony_ci if (!(reg_value & MASK_H_LOCK)) 3858c2ecf20Sopenharmony_ci *status |= V4L2_IN_ST_NO_H_LOCK; 3868c2ecf20Sopenharmony_ci if (!(reg_value & MASK_V_LOCK)) 3878c2ecf20Sopenharmony_ci *status |= V4L2_IN_ST_NO_V_LOCK; 3888c2ecf20Sopenharmony_ci if (!(reg_value & MASK_STD_LOCK)) 3898c2ecf20Sopenharmony_ci *status |= V4L2_IN_ST_NO_STD_LOCK; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci return ret; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic int gs_dv_timings_cap(struct v4l2_subdev *sd, 3958c2ecf20Sopenharmony_ci struct v4l2_dv_timings_cap *cap) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci if (cap->pad != 0) 3988c2ecf20Sopenharmony_ci return -EINVAL; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci *cap = gs_timings_cap; 4018c2ecf20Sopenharmony_ci return 0; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci/* V4L2 core operation handlers */ 4058c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops gs_core_ops = { 4068c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 4078c2ecf20Sopenharmony_ci .g_register = gs_g_register, 4088c2ecf20Sopenharmony_ci .s_register = gs_s_register, 4098c2ecf20Sopenharmony_ci#endif 4108c2ecf20Sopenharmony_ci}; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops gs_video_ops = { 4138c2ecf20Sopenharmony_ci .s_dv_timings = gs_s_dv_timings, 4148c2ecf20Sopenharmony_ci .g_dv_timings = gs_g_dv_timings, 4158c2ecf20Sopenharmony_ci .s_stream = gs_s_stream, 4168c2ecf20Sopenharmony_ci .g_input_status = gs_g_input_status, 4178c2ecf20Sopenharmony_ci .query_dv_timings = gs_query_dv_timings, 4188c2ecf20Sopenharmony_ci}; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops gs_pad_ops = { 4218c2ecf20Sopenharmony_ci .enum_dv_timings = gs_enum_dv_timings, 4228c2ecf20Sopenharmony_ci .dv_timings_cap = gs_dv_timings_cap, 4238c2ecf20Sopenharmony_ci}; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci/* V4L2 top level operation handlers */ 4268c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops gs_ops = { 4278c2ecf20Sopenharmony_ci .core = &gs_core_ops, 4288c2ecf20Sopenharmony_ci .video = &gs_video_ops, 4298c2ecf20Sopenharmony_ci .pad = &gs_pad_ops, 4308c2ecf20Sopenharmony_ci}; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic int gs_probe(struct spi_device *spi) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci int ret; 4358c2ecf20Sopenharmony_ci struct gs *gs; 4368c2ecf20Sopenharmony_ci struct v4l2_subdev *sd; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci gs = devm_kzalloc(&spi->dev, sizeof(struct gs), GFP_KERNEL); 4398c2ecf20Sopenharmony_ci if (!gs) 4408c2ecf20Sopenharmony_ci return -ENOMEM; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci gs->pdev = spi; 4438c2ecf20Sopenharmony_ci sd = &gs->sd; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci spi->mode = SPI_MODE_0; 4468c2ecf20Sopenharmony_ci spi->irq = -1; 4478c2ecf20Sopenharmony_ci spi->max_speed_hz = 10000000; 4488c2ecf20Sopenharmony_ci spi->bits_per_word = 16; 4498c2ecf20Sopenharmony_ci ret = spi_setup(spi); 4508c2ecf20Sopenharmony_ci v4l2_spi_subdev_init(sd, spi, &gs_ops); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci gs->current_timings = reg_fmt[0].format; 4538c2ecf20Sopenharmony_ci gs->enabled = 0; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci /* Set H_CONFIG to SMPTE timings */ 4568c2ecf20Sopenharmony_ci gs_write_register(spi, 0x0, 0x300); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci return ret; 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic int gs_remove(struct spi_device *spi) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = spi_get_drvdata(spi); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci v4l2_device_unregister_subdev(sd); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci return 0; 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic struct spi_driver gs_driver = { 4718c2ecf20Sopenharmony_ci .driver = { 4728c2ecf20Sopenharmony_ci .name = "gs1662", 4738c2ecf20Sopenharmony_ci }, 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci .probe = gs_probe, 4768c2ecf20Sopenharmony_ci .remove = gs_remove, 4778c2ecf20Sopenharmony_ci .id_table = gs_id, 4788c2ecf20Sopenharmony_ci}; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cimodule_spi_driver(gs_driver); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4838c2ecf20Sopenharmony_ciMODULE_AUTHOR("Charles-Antoine Couret <charles-antoine.couret@nexvision.fr>"); 4848c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Gennum GS1662 HD/SD-SDI Serializer driver"); 485