18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Xilinx Test Pattern Generator 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013-2015 Ideas on Board 68c2ecf20Sopenharmony_ci * Copyright (C) 2013-2015 Xilinx, Inc. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> 98c2ecf20Sopenharmony_ci * Laurent Pinchart <laurent.pinchart@ideasonboard.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/of.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/xilinx-v4l2-controls.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <media/v4l2-async.h> 208c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 218c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "xilinx-vip.h" 248c2ecf20Sopenharmony_ci#include "xilinx-vtc.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define XTPG_CTRL_STATUS_SLAVE_ERROR (1 << 16) 278c2ecf20Sopenharmony_ci#define XTPG_CTRL_IRQ_SLAVE_ERROR (1 << 16) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define XTPG_PATTERN_CONTROL 0x0100 308c2ecf20Sopenharmony_ci#define XTPG_PATTERN_MASK (0xf << 0) 318c2ecf20Sopenharmony_ci#define XTPG_PATTERN_CONTROL_CROSS_HAIRS (1 << 4) 328c2ecf20Sopenharmony_ci#define XTPG_PATTERN_CONTROL_MOVING_BOX (1 << 5) 338c2ecf20Sopenharmony_ci#define XTPG_PATTERN_CONTROL_COLOR_MASK_SHIFT 6 348c2ecf20Sopenharmony_ci#define XTPG_PATTERN_CONTROL_COLOR_MASK_MASK (0xf << 6) 358c2ecf20Sopenharmony_ci#define XTPG_PATTERN_CONTROL_STUCK_PIXEL (1 << 9) 368c2ecf20Sopenharmony_ci#define XTPG_PATTERN_CONTROL_NOISE (1 << 10) 378c2ecf20Sopenharmony_ci#define XTPG_PATTERN_CONTROL_MOTION (1 << 12) 388c2ecf20Sopenharmony_ci#define XTPG_MOTION_SPEED 0x0104 398c2ecf20Sopenharmony_ci#define XTPG_CROSS_HAIRS 0x0108 408c2ecf20Sopenharmony_ci#define XTPG_CROSS_HAIRS_ROW_SHIFT 0 418c2ecf20Sopenharmony_ci#define XTPG_CROSS_HAIRS_ROW_MASK (0xfff << 0) 428c2ecf20Sopenharmony_ci#define XTPG_CROSS_HAIRS_COLUMN_SHIFT 16 438c2ecf20Sopenharmony_ci#define XTPG_CROSS_HAIRS_COLUMN_MASK (0xfff << 16) 448c2ecf20Sopenharmony_ci#define XTPG_ZPLATE_HOR_CONTROL 0x010c 458c2ecf20Sopenharmony_ci#define XTPG_ZPLATE_VER_CONTROL 0x0110 468c2ecf20Sopenharmony_ci#define XTPG_ZPLATE_START_SHIFT 0 478c2ecf20Sopenharmony_ci#define XTPG_ZPLATE_START_MASK (0xffff << 0) 488c2ecf20Sopenharmony_ci#define XTPG_ZPLATE_SPEED_SHIFT 16 498c2ecf20Sopenharmony_ci#define XTPG_ZPLATE_SPEED_MASK (0xffff << 16) 508c2ecf20Sopenharmony_ci#define XTPG_BOX_SIZE 0x0114 518c2ecf20Sopenharmony_ci#define XTPG_BOX_COLOR 0x0118 528c2ecf20Sopenharmony_ci#define XTPG_STUCK_PIXEL_THRESH 0x011c 538c2ecf20Sopenharmony_ci#define XTPG_NOISE_GAIN 0x0120 548c2ecf20Sopenharmony_ci#define XTPG_BAYER_PHASE 0x0124 558c2ecf20Sopenharmony_ci#define XTPG_BAYER_PHASE_RGGB 0 568c2ecf20Sopenharmony_ci#define XTPG_BAYER_PHASE_GRBG 1 578c2ecf20Sopenharmony_ci#define XTPG_BAYER_PHASE_GBRG 2 588c2ecf20Sopenharmony_ci#define XTPG_BAYER_PHASE_BGGR 3 598c2ecf20Sopenharmony_ci#define XTPG_BAYER_PHASE_OFF 4 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* 628c2ecf20Sopenharmony_ci * The minimum blanking value is one clock cycle for the front porch, one clock 638c2ecf20Sopenharmony_ci * cycle for the sync pulse and one clock cycle for the back porch. 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_ci#define XTPG_MIN_HBLANK 3 668c2ecf20Sopenharmony_ci#define XTPG_MAX_HBLANK (XVTC_MAX_HSIZE - XVIP_MIN_WIDTH) 678c2ecf20Sopenharmony_ci#define XTPG_MIN_VBLANK 3 688c2ecf20Sopenharmony_ci#define XTPG_MAX_VBLANK (XVTC_MAX_VSIZE - XVIP_MIN_HEIGHT) 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/** 718c2ecf20Sopenharmony_ci * struct xtpg_device - Xilinx Test Pattern Generator device structure 728c2ecf20Sopenharmony_ci * @xvip: Xilinx Video IP device 738c2ecf20Sopenharmony_ci * @pads: media pads 748c2ecf20Sopenharmony_ci * @npads: number of pads (1 or 2) 758c2ecf20Sopenharmony_ci * @has_input: whether an input is connected to the sink pad 768c2ecf20Sopenharmony_ci * @formats: active V4L2 media bus format for each pad 778c2ecf20Sopenharmony_ci * @default_format: default V4L2 media bus format 788c2ecf20Sopenharmony_ci * @vip_format: format information corresponding to the active format 798c2ecf20Sopenharmony_ci * @bayer: boolean flag if TPG is set to any bayer format 808c2ecf20Sopenharmony_ci * @ctrl_handler: control handler 818c2ecf20Sopenharmony_ci * @hblank: horizontal blanking control 828c2ecf20Sopenharmony_ci * @vblank: vertical blanking control 838c2ecf20Sopenharmony_ci * @pattern: test pattern control 848c2ecf20Sopenharmony_ci * @streaming: is the video stream active 858c2ecf20Sopenharmony_ci * @vtc: video timing controller 868c2ecf20Sopenharmony_ci * @vtmux_gpio: video timing mux GPIO 878c2ecf20Sopenharmony_ci */ 888c2ecf20Sopenharmony_cistruct xtpg_device { 898c2ecf20Sopenharmony_ci struct xvip_device xvip; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci struct media_pad pads[2]; 928c2ecf20Sopenharmony_ci unsigned int npads; 938c2ecf20Sopenharmony_ci bool has_input; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt formats[2]; 968c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt default_format; 978c2ecf20Sopenharmony_ci const struct xvip_video_format *vip_format; 988c2ecf20Sopenharmony_ci bool bayer; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler ctrl_handler; 1018c2ecf20Sopenharmony_ci struct v4l2_ctrl *hblank; 1028c2ecf20Sopenharmony_ci struct v4l2_ctrl *vblank; 1038c2ecf20Sopenharmony_ci struct v4l2_ctrl *pattern; 1048c2ecf20Sopenharmony_ci bool streaming; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci struct xvtc_device *vtc; 1078c2ecf20Sopenharmony_ci struct gpio_desc *vtmux_gpio; 1088c2ecf20Sopenharmony_ci}; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic inline struct xtpg_device *to_tpg(struct v4l2_subdev *subdev) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci return container_of(subdev, struct xtpg_device, xvip.subdev); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic u32 xtpg_get_bayer_phase(unsigned int code) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci switch (code) { 1188c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SRGGB8_1X8: 1198c2ecf20Sopenharmony_ci return XTPG_BAYER_PHASE_RGGB; 1208c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SGRBG8_1X8: 1218c2ecf20Sopenharmony_ci return XTPG_BAYER_PHASE_GRBG; 1228c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SGBRG8_1X8: 1238c2ecf20Sopenharmony_ci return XTPG_BAYER_PHASE_GBRG; 1248c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR8_1X8: 1258c2ecf20Sopenharmony_ci return XTPG_BAYER_PHASE_BGGR; 1268c2ecf20Sopenharmony_ci default: 1278c2ecf20Sopenharmony_ci return XTPG_BAYER_PHASE_OFF; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic void __xtpg_update_pattern_control(struct xtpg_device *xtpg, 1328c2ecf20Sopenharmony_ci bool passthrough, bool pattern) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci u32 pattern_mask = (1 << (xtpg->pattern->maximum + 1)) - 1; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* 1378c2ecf20Sopenharmony_ci * If the TPG has no sink pad or no input connected to its sink pad 1388c2ecf20Sopenharmony_ci * passthrough mode can't be enabled. 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ci if (xtpg->npads == 1 || !xtpg->has_input) 1418c2ecf20Sopenharmony_ci passthrough = false; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* If passthrough mode is allowed unmask bit 0. */ 1448c2ecf20Sopenharmony_ci if (passthrough) 1458c2ecf20Sopenharmony_ci pattern_mask &= ~1; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* If test pattern mode is allowed unmask all other bits. */ 1488c2ecf20Sopenharmony_ci if (pattern) 1498c2ecf20Sopenharmony_ci pattern_mask &= 1; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci __v4l2_ctrl_modify_range(xtpg->pattern, 0, xtpg->pattern->maximum, 1528c2ecf20Sopenharmony_ci pattern_mask, pattern ? 9 : 0); 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic void xtpg_update_pattern_control(struct xtpg_device *xtpg, 1568c2ecf20Sopenharmony_ci bool passthrough, bool pattern) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci mutex_lock(xtpg->ctrl_handler.lock); 1598c2ecf20Sopenharmony_ci __xtpg_update_pattern_control(xtpg, passthrough, pattern); 1608c2ecf20Sopenharmony_ci mutex_unlock(xtpg->ctrl_handler.lock); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 1648c2ecf20Sopenharmony_ci * V4L2 Subdevice Video Operations 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic int xtpg_s_stream(struct v4l2_subdev *subdev, int enable) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci struct xtpg_device *xtpg = to_tpg(subdev); 1708c2ecf20Sopenharmony_ci unsigned int width = xtpg->formats[0].width; 1718c2ecf20Sopenharmony_ci unsigned int height = xtpg->formats[0].height; 1728c2ecf20Sopenharmony_ci bool passthrough; 1738c2ecf20Sopenharmony_ci u32 bayer_phase; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (!enable) { 1768c2ecf20Sopenharmony_ci xvip_stop(&xtpg->xvip); 1778c2ecf20Sopenharmony_ci if (xtpg->vtc) 1788c2ecf20Sopenharmony_ci xvtc_generator_stop(xtpg->vtc); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci xtpg_update_pattern_control(xtpg, true, true); 1818c2ecf20Sopenharmony_ci xtpg->streaming = false; 1828c2ecf20Sopenharmony_ci return 0; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci xvip_set_frame_size(&xtpg->xvip, &xtpg->formats[0]); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (xtpg->vtc) { 1888c2ecf20Sopenharmony_ci struct xvtc_config config = { 1898c2ecf20Sopenharmony_ci .hblank_start = width, 1908c2ecf20Sopenharmony_ci .hsync_start = width + 1, 1918c2ecf20Sopenharmony_ci .vblank_start = height, 1928c2ecf20Sopenharmony_ci .vsync_start = height + 1, 1938c2ecf20Sopenharmony_ci }; 1948c2ecf20Sopenharmony_ci unsigned int htotal; 1958c2ecf20Sopenharmony_ci unsigned int vtotal; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci htotal = min_t(unsigned int, XVTC_MAX_HSIZE, 1988c2ecf20Sopenharmony_ci v4l2_ctrl_g_ctrl(xtpg->hblank) + width); 1998c2ecf20Sopenharmony_ci vtotal = min_t(unsigned int, XVTC_MAX_VSIZE, 2008c2ecf20Sopenharmony_ci v4l2_ctrl_g_ctrl(xtpg->vblank) + height); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci config.hsync_end = htotal - 1; 2038c2ecf20Sopenharmony_ci config.hsize = htotal; 2048c2ecf20Sopenharmony_ci config.vsync_end = vtotal - 1; 2058c2ecf20Sopenharmony_ci config.vsize = vtotal; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci xvtc_generator_start(xtpg->vtc, &config); 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* 2118c2ecf20Sopenharmony_ci * Configure the bayer phase and video timing mux based on the 2128c2ecf20Sopenharmony_ci * operation mode (passthrough or test pattern generation). The test 2138c2ecf20Sopenharmony_ci * pattern can be modified by the control set handler, we thus need to 2148c2ecf20Sopenharmony_ci * take the control lock here to avoid races. 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ci mutex_lock(xtpg->ctrl_handler.lock); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci xvip_clr_and_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, 2198c2ecf20Sopenharmony_ci XTPG_PATTERN_MASK, xtpg->pattern->cur.val); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* 2228c2ecf20Sopenharmony_ci * Switching between passthrough and test pattern generation modes isn't 2238c2ecf20Sopenharmony_ci * allowed during streaming, update the control range accordingly. 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_ci passthrough = xtpg->pattern->cur.val == 0; 2268c2ecf20Sopenharmony_ci __xtpg_update_pattern_control(xtpg, passthrough, !passthrough); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci xtpg->streaming = true; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci mutex_unlock(xtpg->ctrl_handler.lock); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* 2338c2ecf20Sopenharmony_ci * For TPG v5.0, the bayer phase needs to be off for the pass through 2348c2ecf20Sopenharmony_ci * mode, otherwise the external input would be subsampled. 2358c2ecf20Sopenharmony_ci */ 2368c2ecf20Sopenharmony_ci bayer_phase = passthrough ? XTPG_BAYER_PHASE_OFF 2378c2ecf20Sopenharmony_ci : xtpg_get_bayer_phase(xtpg->formats[0].code); 2388c2ecf20Sopenharmony_ci xvip_write(&xtpg->xvip, XTPG_BAYER_PHASE, bayer_phase); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (xtpg->vtmux_gpio) 2418c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(xtpg->vtmux_gpio, !passthrough); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci xvip_start(&xtpg->xvip); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return 0; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 2498c2ecf20Sopenharmony_ci * V4L2 Subdevice Pad Operations 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic struct v4l2_mbus_framefmt * 2538c2ecf20Sopenharmony_ci__xtpg_get_pad_format(struct xtpg_device *xtpg, 2548c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 2558c2ecf20Sopenharmony_ci unsigned int pad, u32 which) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci switch (which) { 2588c2ecf20Sopenharmony_ci case V4L2_SUBDEV_FORMAT_TRY: 2598c2ecf20Sopenharmony_ci return v4l2_subdev_get_try_format(&xtpg->xvip.subdev, cfg, pad); 2608c2ecf20Sopenharmony_ci case V4L2_SUBDEV_FORMAT_ACTIVE: 2618c2ecf20Sopenharmony_ci return &xtpg->formats[pad]; 2628c2ecf20Sopenharmony_ci default: 2638c2ecf20Sopenharmony_ci return NULL; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic int xtpg_get_format(struct v4l2_subdev *subdev, 2688c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 2698c2ecf20Sopenharmony_ci struct v4l2_subdev_format *fmt) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct xtpg_device *xtpg = to_tpg(subdev); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci fmt->format = *__xtpg_get_pad_format(xtpg, cfg, fmt->pad, fmt->which); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return 0; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic int xtpg_set_format(struct v4l2_subdev *subdev, 2798c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 2808c2ecf20Sopenharmony_ci struct v4l2_subdev_format *fmt) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci struct xtpg_device *xtpg = to_tpg(subdev); 2838c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *__format; 2848c2ecf20Sopenharmony_ci u32 bayer_phase; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci __format = __xtpg_get_pad_format(xtpg, cfg, fmt->pad, fmt->which); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* In two pads mode the source pad format is always identical to the 2898c2ecf20Sopenharmony_ci * sink pad format. 2908c2ecf20Sopenharmony_ci */ 2918c2ecf20Sopenharmony_ci if (xtpg->npads == 2 && fmt->pad == 1) { 2928c2ecf20Sopenharmony_ci fmt->format = *__format; 2938c2ecf20Sopenharmony_ci return 0; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* Bayer phase is configurable at runtime */ 2978c2ecf20Sopenharmony_ci if (xtpg->bayer) { 2988c2ecf20Sopenharmony_ci bayer_phase = xtpg_get_bayer_phase(fmt->format.code); 2998c2ecf20Sopenharmony_ci if (bayer_phase != XTPG_BAYER_PHASE_OFF) 3008c2ecf20Sopenharmony_ci __format->code = fmt->format.code; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci xvip_set_format_size(__format, fmt); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci fmt->format = *__format; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci /* Propagate the format to the source pad. */ 3088c2ecf20Sopenharmony_ci if (xtpg->npads == 2) { 3098c2ecf20Sopenharmony_ci __format = __xtpg_get_pad_format(xtpg, cfg, 1, fmt->which); 3108c2ecf20Sopenharmony_ci *__format = fmt->format; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci return 0; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 3178c2ecf20Sopenharmony_ci * V4L2 Subdevice Operations 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic int xtpg_enum_frame_size(struct v4l2_subdev *subdev, 3218c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 3228c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_size_enum *fse) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *format; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci format = v4l2_subdev_get_try_format(subdev, cfg, fse->pad); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (fse->index || fse->code != format->code) 3298c2ecf20Sopenharmony_ci return -EINVAL; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* Min / max values for pad 0 is always fixed in both one and two pads 3328c2ecf20Sopenharmony_ci * modes. In two pads mode, the source pad(= 1) size is identical to 3338c2ecf20Sopenharmony_ci * the sink pad size */ 3348c2ecf20Sopenharmony_ci if (fse->pad == 0) { 3358c2ecf20Sopenharmony_ci fse->min_width = XVIP_MIN_WIDTH; 3368c2ecf20Sopenharmony_ci fse->max_width = XVIP_MAX_WIDTH; 3378c2ecf20Sopenharmony_ci fse->min_height = XVIP_MIN_HEIGHT; 3388c2ecf20Sopenharmony_ci fse->max_height = XVIP_MAX_HEIGHT; 3398c2ecf20Sopenharmony_ci } else { 3408c2ecf20Sopenharmony_ci fse->min_width = format->width; 3418c2ecf20Sopenharmony_ci fse->max_width = format->width; 3428c2ecf20Sopenharmony_ci fse->min_height = format->height; 3438c2ecf20Sopenharmony_ci fse->max_height = format->height; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci return 0; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic int xtpg_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci struct xtpg_device *xtpg = to_tpg(subdev); 3528c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *format; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci format = v4l2_subdev_get_try_format(subdev, fh->pad, 0); 3558c2ecf20Sopenharmony_ci *format = xtpg->default_format; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (xtpg->npads == 2) { 3588c2ecf20Sopenharmony_ci format = v4l2_subdev_get_try_format(subdev, fh->pad, 1); 3598c2ecf20Sopenharmony_ci *format = xtpg->default_format; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic int xtpg_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci return 0; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic int xtpg_s_ctrl(struct v4l2_ctrl *ctrl) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct xtpg_device *xtpg = container_of(ctrl->handler, 3738c2ecf20Sopenharmony_ci struct xtpg_device, 3748c2ecf20Sopenharmony_ci ctrl_handler); 3758c2ecf20Sopenharmony_ci switch (ctrl->id) { 3768c2ecf20Sopenharmony_ci case V4L2_CID_TEST_PATTERN: 3778c2ecf20Sopenharmony_ci xvip_clr_and_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, 3788c2ecf20Sopenharmony_ci XTPG_PATTERN_MASK, ctrl->val); 3798c2ecf20Sopenharmony_ci return 0; 3808c2ecf20Sopenharmony_ci case V4L2_CID_XILINX_TPG_CROSS_HAIRS: 3818c2ecf20Sopenharmony_ci xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, 3828c2ecf20Sopenharmony_ci XTPG_PATTERN_CONTROL_CROSS_HAIRS, ctrl->val); 3838c2ecf20Sopenharmony_ci return 0; 3848c2ecf20Sopenharmony_ci case V4L2_CID_XILINX_TPG_MOVING_BOX: 3858c2ecf20Sopenharmony_ci xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, 3868c2ecf20Sopenharmony_ci XTPG_PATTERN_CONTROL_MOVING_BOX, ctrl->val); 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci case V4L2_CID_XILINX_TPG_COLOR_MASK: 3898c2ecf20Sopenharmony_ci xvip_clr_and_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, 3908c2ecf20Sopenharmony_ci XTPG_PATTERN_CONTROL_COLOR_MASK_MASK, 3918c2ecf20Sopenharmony_ci ctrl->val << 3928c2ecf20Sopenharmony_ci XTPG_PATTERN_CONTROL_COLOR_MASK_SHIFT); 3938c2ecf20Sopenharmony_ci return 0; 3948c2ecf20Sopenharmony_ci case V4L2_CID_XILINX_TPG_STUCK_PIXEL: 3958c2ecf20Sopenharmony_ci xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, 3968c2ecf20Sopenharmony_ci XTPG_PATTERN_CONTROL_STUCK_PIXEL, ctrl->val); 3978c2ecf20Sopenharmony_ci return 0; 3988c2ecf20Sopenharmony_ci case V4L2_CID_XILINX_TPG_NOISE: 3998c2ecf20Sopenharmony_ci xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, 4008c2ecf20Sopenharmony_ci XTPG_PATTERN_CONTROL_NOISE, ctrl->val); 4018c2ecf20Sopenharmony_ci return 0; 4028c2ecf20Sopenharmony_ci case V4L2_CID_XILINX_TPG_MOTION: 4038c2ecf20Sopenharmony_ci xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, 4048c2ecf20Sopenharmony_ci XTPG_PATTERN_CONTROL_MOTION, ctrl->val); 4058c2ecf20Sopenharmony_ci return 0; 4068c2ecf20Sopenharmony_ci case V4L2_CID_XILINX_TPG_MOTION_SPEED: 4078c2ecf20Sopenharmony_ci xvip_write(&xtpg->xvip, XTPG_MOTION_SPEED, ctrl->val); 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci case V4L2_CID_XILINX_TPG_CROSS_HAIR_ROW: 4108c2ecf20Sopenharmony_ci xvip_clr_and_set(&xtpg->xvip, XTPG_CROSS_HAIRS, 4118c2ecf20Sopenharmony_ci XTPG_CROSS_HAIRS_ROW_MASK, 4128c2ecf20Sopenharmony_ci ctrl->val << XTPG_CROSS_HAIRS_ROW_SHIFT); 4138c2ecf20Sopenharmony_ci return 0; 4148c2ecf20Sopenharmony_ci case V4L2_CID_XILINX_TPG_CROSS_HAIR_COLUMN: 4158c2ecf20Sopenharmony_ci xvip_clr_and_set(&xtpg->xvip, XTPG_CROSS_HAIRS, 4168c2ecf20Sopenharmony_ci XTPG_CROSS_HAIRS_COLUMN_MASK, 4178c2ecf20Sopenharmony_ci ctrl->val << XTPG_CROSS_HAIRS_COLUMN_SHIFT); 4188c2ecf20Sopenharmony_ci return 0; 4198c2ecf20Sopenharmony_ci case V4L2_CID_XILINX_TPG_ZPLATE_HOR_START: 4208c2ecf20Sopenharmony_ci xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_HOR_CONTROL, 4218c2ecf20Sopenharmony_ci XTPG_ZPLATE_START_MASK, 4228c2ecf20Sopenharmony_ci ctrl->val << XTPG_ZPLATE_START_SHIFT); 4238c2ecf20Sopenharmony_ci return 0; 4248c2ecf20Sopenharmony_ci case V4L2_CID_XILINX_TPG_ZPLATE_HOR_SPEED: 4258c2ecf20Sopenharmony_ci xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_HOR_CONTROL, 4268c2ecf20Sopenharmony_ci XTPG_ZPLATE_SPEED_MASK, 4278c2ecf20Sopenharmony_ci ctrl->val << XTPG_ZPLATE_SPEED_SHIFT); 4288c2ecf20Sopenharmony_ci return 0; 4298c2ecf20Sopenharmony_ci case V4L2_CID_XILINX_TPG_ZPLATE_VER_START: 4308c2ecf20Sopenharmony_ci xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_VER_CONTROL, 4318c2ecf20Sopenharmony_ci XTPG_ZPLATE_START_MASK, 4328c2ecf20Sopenharmony_ci ctrl->val << XTPG_ZPLATE_START_SHIFT); 4338c2ecf20Sopenharmony_ci return 0; 4348c2ecf20Sopenharmony_ci case V4L2_CID_XILINX_TPG_ZPLATE_VER_SPEED: 4358c2ecf20Sopenharmony_ci xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_VER_CONTROL, 4368c2ecf20Sopenharmony_ci XTPG_ZPLATE_SPEED_MASK, 4378c2ecf20Sopenharmony_ci ctrl->val << XTPG_ZPLATE_SPEED_SHIFT); 4388c2ecf20Sopenharmony_ci return 0; 4398c2ecf20Sopenharmony_ci case V4L2_CID_XILINX_TPG_BOX_SIZE: 4408c2ecf20Sopenharmony_ci xvip_write(&xtpg->xvip, XTPG_BOX_SIZE, ctrl->val); 4418c2ecf20Sopenharmony_ci return 0; 4428c2ecf20Sopenharmony_ci case V4L2_CID_XILINX_TPG_BOX_COLOR: 4438c2ecf20Sopenharmony_ci xvip_write(&xtpg->xvip, XTPG_BOX_COLOR, ctrl->val); 4448c2ecf20Sopenharmony_ci return 0; 4458c2ecf20Sopenharmony_ci case V4L2_CID_XILINX_TPG_STUCK_PIXEL_THRESH: 4468c2ecf20Sopenharmony_ci xvip_write(&xtpg->xvip, XTPG_STUCK_PIXEL_THRESH, ctrl->val); 4478c2ecf20Sopenharmony_ci return 0; 4488c2ecf20Sopenharmony_ci case V4L2_CID_XILINX_TPG_NOISE_GAIN: 4498c2ecf20Sopenharmony_ci xvip_write(&xtpg->xvip, XTPG_NOISE_GAIN, ctrl->val); 4508c2ecf20Sopenharmony_ci return 0; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci return 0; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops xtpg_ctrl_ops = { 4578c2ecf20Sopenharmony_ci .s_ctrl = xtpg_s_ctrl, 4588c2ecf20Sopenharmony_ci}; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops xtpg_core_ops = { 4618c2ecf20Sopenharmony_ci}; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops xtpg_video_ops = { 4648c2ecf20Sopenharmony_ci .s_stream = xtpg_s_stream, 4658c2ecf20Sopenharmony_ci}; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops xtpg_pad_ops = { 4688c2ecf20Sopenharmony_ci .enum_mbus_code = xvip_enum_mbus_code, 4698c2ecf20Sopenharmony_ci .enum_frame_size = xtpg_enum_frame_size, 4708c2ecf20Sopenharmony_ci .get_fmt = xtpg_get_format, 4718c2ecf20Sopenharmony_ci .set_fmt = xtpg_set_format, 4728c2ecf20Sopenharmony_ci}; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops xtpg_ops = { 4758c2ecf20Sopenharmony_ci .core = &xtpg_core_ops, 4768c2ecf20Sopenharmony_ci .video = &xtpg_video_ops, 4778c2ecf20Sopenharmony_ci .pad = &xtpg_pad_ops, 4788c2ecf20Sopenharmony_ci}; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_internal_ops xtpg_internal_ops = { 4818c2ecf20Sopenharmony_ci .open = xtpg_open, 4828c2ecf20Sopenharmony_ci .close = xtpg_close, 4838c2ecf20Sopenharmony_ci}; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci/* 4868c2ecf20Sopenharmony_ci * Control Config 4878c2ecf20Sopenharmony_ci */ 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic const char *const xtpg_pattern_strings[] = { 4908c2ecf20Sopenharmony_ci "Passthrough", 4918c2ecf20Sopenharmony_ci "Horizontal Ramp", 4928c2ecf20Sopenharmony_ci "Vertical Ramp", 4938c2ecf20Sopenharmony_ci "Temporal Ramp", 4948c2ecf20Sopenharmony_ci "Solid Red", 4958c2ecf20Sopenharmony_ci "Solid Green", 4968c2ecf20Sopenharmony_ci "Solid Blue", 4978c2ecf20Sopenharmony_ci "Solid Black", 4988c2ecf20Sopenharmony_ci "Solid White", 4998c2ecf20Sopenharmony_ci "Color Bars", 5008c2ecf20Sopenharmony_ci "Zone Plate", 5018c2ecf20Sopenharmony_ci "Tartan Color Bars", 5028c2ecf20Sopenharmony_ci "Cross Hatch", 5038c2ecf20Sopenharmony_ci "None", 5048c2ecf20Sopenharmony_ci "Vertical/Horizontal Ramps", 5058c2ecf20Sopenharmony_ci "Black/White Checker Board", 5068c2ecf20Sopenharmony_ci}; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic struct v4l2_ctrl_config xtpg_ctrls[] = { 5098c2ecf20Sopenharmony_ci { 5108c2ecf20Sopenharmony_ci .ops = &xtpg_ctrl_ops, 5118c2ecf20Sopenharmony_ci .id = V4L2_CID_XILINX_TPG_CROSS_HAIRS, 5128c2ecf20Sopenharmony_ci .name = "Test Pattern: Cross Hairs", 5138c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_BOOLEAN, 5148c2ecf20Sopenharmony_ci .min = false, 5158c2ecf20Sopenharmony_ci .max = true, 5168c2ecf20Sopenharmony_ci .step = 1, 5178c2ecf20Sopenharmony_ci .def = 0, 5188c2ecf20Sopenharmony_ci }, { 5198c2ecf20Sopenharmony_ci .ops = &xtpg_ctrl_ops, 5208c2ecf20Sopenharmony_ci .id = V4L2_CID_XILINX_TPG_MOVING_BOX, 5218c2ecf20Sopenharmony_ci .name = "Test Pattern: Moving Box", 5228c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_BOOLEAN, 5238c2ecf20Sopenharmony_ci .min = false, 5248c2ecf20Sopenharmony_ci .max = true, 5258c2ecf20Sopenharmony_ci .step = 1, 5268c2ecf20Sopenharmony_ci .def = 0, 5278c2ecf20Sopenharmony_ci }, { 5288c2ecf20Sopenharmony_ci .ops = &xtpg_ctrl_ops, 5298c2ecf20Sopenharmony_ci .id = V4L2_CID_XILINX_TPG_COLOR_MASK, 5308c2ecf20Sopenharmony_ci .name = "Test Pattern: Color Mask", 5318c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_BITMASK, 5328c2ecf20Sopenharmony_ci .min = 0, 5338c2ecf20Sopenharmony_ci .max = 0xf, 5348c2ecf20Sopenharmony_ci .def = 0, 5358c2ecf20Sopenharmony_ci }, { 5368c2ecf20Sopenharmony_ci .ops = &xtpg_ctrl_ops, 5378c2ecf20Sopenharmony_ci .id = V4L2_CID_XILINX_TPG_STUCK_PIXEL, 5388c2ecf20Sopenharmony_ci .name = "Test Pattern: Stuck Pixel", 5398c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_BOOLEAN, 5408c2ecf20Sopenharmony_ci .min = false, 5418c2ecf20Sopenharmony_ci .max = true, 5428c2ecf20Sopenharmony_ci .step = 1, 5438c2ecf20Sopenharmony_ci .def = 0, 5448c2ecf20Sopenharmony_ci }, { 5458c2ecf20Sopenharmony_ci .ops = &xtpg_ctrl_ops, 5468c2ecf20Sopenharmony_ci .id = V4L2_CID_XILINX_TPG_NOISE, 5478c2ecf20Sopenharmony_ci .name = "Test Pattern: Noise", 5488c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_BOOLEAN, 5498c2ecf20Sopenharmony_ci .min = false, 5508c2ecf20Sopenharmony_ci .max = true, 5518c2ecf20Sopenharmony_ci .step = 1, 5528c2ecf20Sopenharmony_ci .def = 0, 5538c2ecf20Sopenharmony_ci }, { 5548c2ecf20Sopenharmony_ci .ops = &xtpg_ctrl_ops, 5558c2ecf20Sopenharmony_ci .id = V4L2_CID_XILINX_TPG_MOTION, 5568c2ecf20Sopenharmony_ci .name = "Test Pattern: Motion", 5578c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_BOOLEAN, 5588c2ecf20Sopenharmony_ci .min = false, 5598c2ecf20Sopenharmony_ci .max = true, 5608c2ecf20Sopenharmony_ci .step = 1, 5618c2ecf20Sopenharmony_ci .def = 0, 5628c2ecf20Sopenharmony_ci }, { 5638c2ecf20Sopenharmony_ci .ops = &xtpg_ctrl_ops, 5648c2ecf20Sopenharmony_ci .id = V4L2_CID_XILINX_TPG_MOTION_SPEED, 5658c2ecf20Sopenharmony_ci .name = "Test Pattern: Motion Speed", 5668c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 5678c2ecf20Sopenharmony_ci .min = 0, 5688c2ecf20Sopenharmony_ci .max = (1 << 8) - 1, 5698c2ecf20Sopenharmony_ci .step = 1, 5708c2ecf20Sopenharmony_ci .def = 4, 5718c2ecf20Sopenharmony_ci .flags = V4L2_CTRL_FLAG_SLIDER, 5728c2ecf20Sopenharmony_ci }, { 5738c2ecf20Sopenharmony_ci .ops = &xtpg_ctrl_ops, 5748c2ecf20Sopenharmony_ci .id = V4L2_CID_XILINX_TPG_CROSS_HAIR_ROW, 5758c2ecf20Sopenharmony_ci .name = "Test Pattern: Cross Hairs Row", 5768c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 5778c2ecf20Sopenharmony_ci .min = 0, 5788c2ecf20Sopenharmony_ci .max = (1 << 12) - 1, 5798c2ecf20Sopenharmony_ci .step = 1, 5808c2ecf20Sopenharmony_ci .def = 0x64, 5818c2ecf20Sopenharmony_ci .flags = V4L2_CTRL_FLAG_SLIDER, 5828c2ecf20Sopenharmony_ci }, { 5838c2ecf20Sopenharmony_ci .ops = &xtpg_ctrl_ops, 5848c2ecf20Sopenharmony_ci .id = V4L2_CID_XILINX_TPG_CROSS_HAIR_COLUMN, 5858c2ecf20Sopenharmony_ci .name = "Test Pattern: Cross Hairs Column", 5868c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 5878c2ecf20Sopenharmony_ci .min = 0, 5888c2ecf20Sopenharmony_ci .max = (1 << 12) - 1, 5898c2ecf20Sopenharmony_ci .step = 1, 5908c2ecf20Sopenharmony_ci .def = 0x64, 5918c2ecf20Sopenharmony_ci .flags = V4L2_CTRL_FLAG_SLIDER, 5928c2ecf20Sopenharmony_ci }, { 5938c2ecf20Sopenharmony_ci .ops = &xtpg_ctrl_ops, 5948c2ecf20Sopenharmony_ci .id = V4L2_CID_XILINX_TPG_ZPLATE_HOR_START, 5958c2ecf20Sopenharmony_ci .name = "Test Pattern: Zplate Horizontal Start Pos", 5968c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 5978c2ecf20Sopenharmony_ci .min = 0, 5988c2ecf20Sopenharmony_ci .max = (1 << 16) - 1, 5998c2ecf20Sopenharmony_ci .step = 1, 6008c2ecf20Sopenharmony_ci .def = 0x1e, 6018c2ecf20Sopenharmony_ci .flags = V4L2_CTRL_FLAG_SLIDER, 6028c2ecf20Sopenharmony_ci }, { 6038c2ecf20Sopenharmony_ci .ops = &xtpg_ctrl_ops, 6048c2ecf20Sopenharmony_ci .id = V4L2_CID_XILINX_TPG_ZPLATE_HOR_SPEED, 6058c2ecf20Sopenharmony_ci .name = "Test Pattern: Zplate Horizontal Speed", 6068c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 6078c2ecf20Sopenharmony_ci .min = 0, 6088c2ecf20Sopenharmony_ci .max = (1 << 16) - 1, 6098c2ecf20Sopenharmony_ci .step = 1, 6108c2ecf20Sopenharmony_ci .def = 0, 6118c2ecf20Sopenharmony_ci .flags = V4L2_CTRL_FLAG_SLIDER, 6128c2ecf20Sopenharmony_ci }, { 6138c2ecf20Sopenharmony_ci .ops = &xtpg_ctrl_ops, 6148c2ecf20Sopenharmony_ci .id = V4L2_CID_XILINX_TPG_ZPLATE_VER_START, 6158c2ecf20Sopenharmony_ci .name = "Test Pattern: Zplate Vertical Start Pos", 6168c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 6178c2ecf20Sopenharmony_ci .min = 0, 6188c2ecf20Sopenharmony_ci .max = (1 << 16) - 1, 6198c2ecf20Sopenharmony_ci .step = 1, 6208c2ecf20Sopenharmony_ci .def = 1, 6218c2ecf20Sopenharmony_ci .flags = V4L2_CTRL_FLAG_SLIDER, 6228c2ecf20Sopenharmony_ci }, { 6238c2ecf20Sopenharmony_ci .ops = &xtpg_ctrl_ops, 6248c2ecf20Sopenharmony_ci .id = V4L2_CID_XILINX_TPG_ZPLATE_VER_SPEED, 6258c2ecf20Sopenharmony_ci .name = "Test Pattern: Zplate Vertical Speed", 6268c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 6278c2ecf20Sopenharmony_ci .min = 0, 6288c2ecf20Sopenharmony_ci .max = (1 << 16) - 1, 6298c2ecf20Sopenharmony_ci .step = 1, 6308c2ecf20Sopenharmony_ci .def = 0, 6318c2ecf20Sopenharmony_ci .flags = V4L2_CTRL_FLAG_SLIDER, 6328c2ecf20Sopenharmony_ci }, { 6338c2ecf20Sopenharmony_ci .ops = &xtpg_ctrl_ops, 6348c2ecf20Sopenharmony_ci .id = V4L2_CID_XILINX_TPG_BOX_SIZE, 6358c2ecf20Sopenharmony_ci .name = "Test Pattern: Box Size", 6368c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 6378c2ecf20Sopenharmony_ci .min = 0, 6388c2ecf20Sopenharmony_ci .max = (1 << 12) - 1, 6398c2ecf20Sopenharmony_ci .step = 1, 6408c2ecf20Sopenharmony_ci .def = 0x32, 6418c2ecf20Sopenharmony_ci .flags = V4L2_CTRL_FLAG_SLIDER, 6428c2ecf20Sopenharmony_ci }, { 6438c2ecf20Sopenharmony_ci .ops = &xtpg_ctrl_ops, 6448c2ecf20Sopenharmony_ci .id = V4L2_CID_XILINX_TPG_BOX_COLOR, 6458c2ecf20Sopenharmony_ci .name = "Test Pattern: Box Color(RGB)", 6468c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 6478c2ecf20Sopenharmony_ci .min = 0, 6488c2ecf20Sopenharmony_ci .max = (1 << 24) - 1, 6498c2ecf20Sopenharmony_ci .step = 1, 6508c2ecf20Sopenharmony_ci .def = 0, 6518c2ecf20Sopenharmony_ci }, { 6528c2ecf20Sopenharmony_ci .ops = &xtpg_ctrl_ops, 6538c2ecf20Sopenharmony_ci .id = V4L2_CID_XILINX_TPG_STUCK_PIXEL_THRESH, 6548c2ecf20Sopenharmony_ci .name = "Test Pattern: Stuck Pixel threshold", 6558c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 6568c2ecf20Sopenharmony_ci .min = 0, 6578c2ecf20Sopenharmony_ci .max = (1 << 16) - 1, 6588c2ecf20Sopenharmony_ci .step = 1, 6598c2ecf20Sopenharmony_ci .def = 0, 6608c2ecf20Sopenharmony_ci .flags = V4L2_CTRL_FLAG_SLIDER, 6618c2ecf20Sopenharmony_ci }, { 6628c2ecf20Sopenharmony_ci .ops = &xtpg_ctrl_ops, 6638c2ecf20Sopenharmony_ci .id = V4L2_CID_XILINX_TPG_NOISE_GAIN, 6648c2ecf20Sopenharmony_ci .name = "Test Pattern: Noise Gain", 6658c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 6668c2ecf20Sopenharmony_ci .min = 0, 6678c2ecf20Sopenharmony_ci .max = (1 << 8) - 1, 6688c2ecf20Sopenharmony_ci .step = 1, 6698c2ecf20Sopenharmony_ci .def = 0, 6708c2ecf20Sopenharmony_ci .flags = V4L2_CTRL_FLAG_SLIDER, 6718c2ecf20Sopenharmony_ci }, 6728c2ecf20Sopenharmony_ci}; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 6758c2ecf20Sopenharmony_ci * Media Operations 6768c2ecf20Sopenharmony_ci */ 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_cistatic const struct media_entity_operations xtpg_media_ops = { 6798c2ecf20Sopenharmony_ci .link_validate = v4l2_subdev_link_validate, 6808c2ecf20Sopenharmony_ci}; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 6838c2ecf20Sopenharmony_ci * Power Management 6848c2ecf20Sopenharmony_ci */ 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_cistatic int __maybe_unused xtpg_pm_suspend(struct device *dev) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci struct xtpg_device *xtpg = dev_get_drvdata(dev); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci xvip_suspend(&xtpg->xvip); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci return 0; 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_cistatic int __maybe_unused xtpg_pm_resume(struct device *dev) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci struct xtpg_device *xtpg = dev_get_drvdata(dev); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci xvip_resume(&xtpg->xvip); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci return 0; 7028c2ecf20Sopenharmony_ci} 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 7058c2ecf20Sopenharmony_ci * Platform Device Driver 7068c2ecf20Sopenharmony_ci */ 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_cistatic int xtpg_parse_of(struct xtpg_device *xtpg) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci struct device *dev = xtpg->xvip.dev; 7118c2ecf20Sopenharmony_ci struct device_node *node = xtpg->xvip.dev->of_node; 7128c2ecf20Sopenharmony_ci struct device_node *ports; 7138c2ecf20Sopenharmony_ci struct device_node *port; 7148c2ecf20Sopenharmony_ci unsigned int nports = 0; 7158c2ecf20Sopenharmony_ci bool has_endpoint = false; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci ports = of_get_child_by_name(node, "ports"); 7188c2ecf20Sopenharmony_ci if (ports == NULL) 7198c2ecf20Sopenharmony_ci ports = node; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci for_each_child_of_node(ports, port) { 7228c2ecf20Sopenharmony_ci const struct xvip_video_format *format; 7238c2ecf20Sopenharmony_ci struct device_node *endpoint; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci if (!of_node_name_eq(port, "port")) 7268c2ecf20Sopenharmony_ci continue; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci format = xvip_of_get_format(port); 7298c2ecf20Sopenharmony_ci if (IS_ERR(format)) { 7308c2ecf20Sopenharmony_ci dev_err(dev, "invalid format in DT"); 7318c2ecf20Sopenharmony_ci of_node_put(port); 7328c2ecf20Sopenharmony_ci return PTR_ERR(format); 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci /* Get and check the format description */ 7368c2ecf20Sopenharmony_ci if (!xtpg->vip_format) { 7378c2ecf20Sopenharmony_ci xtpg->vip_format = format; 7388c2ecf20Sopenharmony_ci } else if (xtpg->vip_format != format) { 7398c2ecf20Sopenharmony_ci dev_err(dev, "in/out format mismatch in DT"); 7408c2ecf20Sopenharmony_ci of_node_put(port); 7418c2ecf20Sopenharmony_ci return -EINVAL; 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (nports == 0) { 7458c2ecf20Sopenharmony_ci endpoint = of_get_next_child(port, NULL); 7468c2ecf20Sopenharmony_ci if (endpoint) 7478c2ecf20Sopenharmony_ci has_endpoint = true; 7488c2ecf20Sopenharmony_ci of_node_put(endpoint); 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci /* Count the number of ports. */ 7528c2ecf20Sopenharmony_ci nports++; 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci if (nports != 1 && nports != 2) { 7568c2ecf20Sopenharmony_ci dev_err(dev, "invalid number of ports %u\n", nports); 7578c2ecf20Sopenharmony_ci return -EINVAL; 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci xtpg->npads = nports; 7618c2ecf20Sopenharmony_ci if (nports == 2 && has_endpoint) 7628c2ecf20Sopenharmony_ci xtpg->has_input = true; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci return 0; 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_cistatic int xtpg_probe(struct platform_device *pdev) 7688c2ecf20Sopenharmony_ci{ 7698c2ecf20Sopenharmony_ci struct v4l2_subdev *subdev; 7708c2ecf20Sopenharmony_ci struct xtpg_device *xtpg; 7718c2ecf20Sopenharmony_ci u32 i, bayer_phase; 7728c2ecf20Sopenharmony_ci int ret; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci xtpg = devm_kzalloc(&pdev->dev, sizeof(*xtpg), GFP_KERNEL); 7758c2ecf20Sopenharmony_ci if (!xtpg) 7768c2ecf20Sopenharmony_ci return -ENOMEM; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci xtpg->xvip.dev = &pdev->dev; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci ret = xtpg_parse_of(xtpg); 7818c2ecf20Sopenharmony_ci if (ret < 0) 7828c2ecf20Sopenharmony_ci return ret; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci ret = xvip_init_resources(&xtpg->xvip); 7858c2ecf20Sopenharmony_ci if (ret < 0) 7868c2ecf20Sopenharmony_ci return ret; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci xtpg->vtmux_gpio = devm_gpiod_get_optional(&pdev->dev, "timing", 7898c2ecf20Sopenharmony_ci GPIOD_OUT_HIGH); 7908c2ecf20Sopenharmony_ci if (IS_ERR(xtpg->vtmux_gpio)) { 7918c2ecf20Sopenharmony_ci ret = PTR_ERR(xtpg->vtmux_gpio); 7928c2ecf20Sopenharmony_ci goto error_resource; 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci xtpg->vtc = xvtc_of_get(pdev->dev.of_node); 7968c2ecf20Sopenharmony_ci if (IS_ERR(xtpg->vtc)) { 7978c2ecf20Sopenharmony_ci ret = PTR_ERR(xtpg->vtc); 7988c2ecf20Sopenharmony_ci goto error_resource; 7998c2ecf20Sopenharmony_ci } 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci /* Reset and initialize the core */ 8028c2ecf20Sopenharmony_ci xvip_reset(&xtpg->xvip); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci /* Initialize V4L2 subdevice and media entity. Pad numbers depend on the 8058c2ecf20Sopenharmony_ci * number of pads. 8068c2ecf20Sopenharmony_ci */ 8078c2ecf20Sopenharmony_ci if (xtpg->npads == 2) { 8088c2ecf20Sopenharmony_ci xtpg->pads[0].flags = MEDIA_PAD_FL_SINK; 8098c2ecf20Sopenharmony_ci xtpg->pads[1].flags = MEDIA_PAD_FL_SOURCE; 8108c2ecf20Sopenharmony_ci } else { 8118c2ecf20Sopenharmony_ci xtpg->pads[0].flags = MEDIA_PAD_FL_SOURCE; 8128c2ecf20Sopenharmony_ci } 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci /* Initialize the default format */ 8158c2ecf20Sopenharmony_ci xtpg->default_format.code = xtpg->vip_format->code; 8168c2ecf20Sopenharmony_ci xtpg->default_format.field = V4L2_FIELD_NONE; 8178c2ecf20Sopenharmony_ci xtpg->default_format.colorspace = V4L2_COLORSPACE_SRGB; 8188c2ecf20Sopenharmony_ci xvip_get_frame_size(&xtpg->xvip, &xtpg->default_format); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci bayer_phase = xtpg_get_bayer_phase(xtpg->vip_format->code); 8218c2ecf20Sopenharmony_ci if (bayer_phase != XTPG_BAYER_PHASE_OFF) 8228c2ecf20Sopenharmony_ci xtpg->bayer = true; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci xtpg->formats[0] = xtpg->default_format; 8258c2ecf20Sopenharmony_ci if (xtpg->npads == 2) 8268c2ecf20Sopenharmony_ci xtpg->formats[1] = xtpg->default_format; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci /* Initialize V4L2 subdevice and media entity */ 8298c2ecf20Sopenharmony_ci subdev = &xtpg->xvip.subdev; 8308c2ecf20Sopenharmony_ci v4l2_subdev_init(subdev, &xtpg_ops); 8318c2ecf20Sopenharmony_ci subdev->dev = &pdev->dev; 8328c2ecf20Sopenharmony_ci subdev->internal_ops = &xtpg_internal_ops; 8338c2ecf20Sopenharmony_ci strscpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); 8348c2ecf20Sopenharmony_ci v4l2_set_subdevdata(subdev, xtpg); 8358c2ecf20Sopenharmony_ci subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 8368c2ecf20Sopenharmony_ci subdev->entity.ops = &xtpg_media_ops; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci ret = media_entity_pads_init(&subdev->entity, xtpg->npads, xtpg->pads); 8398c2ecf20Sopenharmony_ci if (ret < 0) 8408c2ecf20Sopenharmony_ci goto error; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&xtpg->ctrl_handler, 3 + ARRAY_SIZE(xtpg_ctrls)); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci xtpg->vblank = v4l2_ctrl_new_std(&xtpg->ctrl_handler, &xtpg_ctrl_ops, 8458c2ecf20Sopenharmony_ci V4L2_CID_VBLANK, XTPG_MIN_VBLANK, 8468c2ecf20Sopenharmony_ci XTPG_MAX_VBLANK, 1, 100); 8478c2ecf20Sopenharmony_ci xtpg->hblank = v4l2_ctrl_new_std(&xtpg->ctrl_handler, &xtpg_ctrl_ops, 8488c2ecf20Sopenharmony_ci V4L2_CID_HBLANK, XTPG_MIN_HBLANK, 8498c2ecf20Sopenharmony_ci XTPG_MAX_HBLANK, 1, 100); 8508c2ecf20Sopenharmony_ci xtpg->pattern = v4l2_ctrl_new_std_menu_items(&xtpg->ctrl_handler, 8518c2ecf20Sopenharmony_ci &xtpg_ctrl_ops, V4L2_CID_TEST_PATTERN, 8528c2ecf20Sopenharmony_ci ARRAY_SIZE(xtpg_pattern_strings) - 1, 8538c2ecf20Sopenharmony_ci 1, 9, xtpg_pattern_strings); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(xtpg_ctrls); i++) 8568c2ecf20Sopenharmony_ci v4l2_ctrl_new_custom(&xtpg->ctrl_handler, &xtpg_ctrls[i], NULL); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci if (xtpg->ctrl_handler.error) { 8598c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to add controls\n"); 8608c2ecf20Sopenharmony_ci ret = xtpg->ctrl_handler.error; 8618c2ecf20Sopenharmony_ci goto error; 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci subdev->ctrl_handler = &xtpg->ctrl_handler; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci xtpg_update_pattern_control(xtpg, true, true); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci ret = v4l2_ctrl_handler_setup(&xtpg->ctrl_handler); 8688c2ecf20Sopenharmony_ci if (ret < 0) { 8698c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to set controls\n"); 8708c2ecf20Sopenharmony_ci goto error; 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, xtpg); 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci xvip_print_version(&xtpg->xvip); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci ret = v4l2_async_register_subdev(subdev); 8788c2ecf20Sopenharmony_ci if (ret < 0) { 8798c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register subdev\n"); 8808c2ecf20Sopenharmony_ci goto error; 8818c2ecf20Sopenharmony_ci } 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci return 0; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_cierror: 8868c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&xtpg->ctrl_handler); 8878c2ecf20Sopenharmony_ci media_entity_cleanup(&subdev->entity); 8888c2ecf20Sopenharmony_ci xvtc_put(xtpg->vtc); 8898c2ecf20Sopenharmony_cierror_resource: 8908c2ecf20Sopenharmony_ci xvip_cleanup_resources(&xtpg->xvip); 8918c2ecf20Sopenharmony_ci return ret; 8928c2ecf20Sopenharmony_ci} 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_cistatic int xtpg_remove(struct platform_device *pdev) 8958c2ecf20Sopenharmony_ci{ 8968c2ecf20Sopenharmony_ci struct xtpg_device *xtpg = platform_get_drvdata(pdev); 8978c2ecf20Sopenharmony_ci struct v4l2_subdev *subdev = &xtpg->xvip.subdev; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci v4l2_async_unregister_subdev(subdev); 9008c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&xtpg->ctrl_handler); 9018c2ecf20Sopenharmony_ci media_entity_cleanup(&subdev->entity); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci xvip_cleanup_resources(&xtpg->xvip); 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci return 0; 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(xtpg_pm_ops, xtpg_pm_suspend, xtpg_pm_resume); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_cistatic const struct of_device_id xtpg_of_id_table[] = { 9118c2ecf20Sopenharmony_ci { .compatible = "xlnx,v-tpg-5.0" }, 9128c2ecf20Sopenharmony_ci { } 9138c2ecf20Sopenharmony_ci}; 9148c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, xtpg_of_id_table); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_cistatic struct platform_driver xtpg_driver = { 9178c2ecf20Sopenharmony_ci .driver = { 9188c2ecf20Sopenharmony_ci .name = "xilinx-tpg", 9198c2ecf20Sopenharmony_ci .pm = &xtpg_pm_ops, 9208c2ecf20Sopenharmony_ci .of_match_table = xtpg_of_id_table, 9218c2ecf20Sopenharmony_ci }, 9228c2ecf20Sopenharmony_ci .probe = xtpg_probe, 9238c2ecf20Sopenharmony_ci .remove = xtpg_remove, 9248c2ecf20Sopenharmony_ci}; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_cimodule_platform_driver(xtpg_driver); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ciMODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); 9298c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Xilinx Test Pattern Generator Driver"); 9308c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 931