18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for MT9M111/MT9M112/MT9M131 CMOS Image Sensor from Micron/Aptina 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci#include <linux/i2c.h> 108c2ecf20Sopenharmony_ci#include <linux/log2.h> 118c2ecf20Sopenharmony_ci#include <linux/gpio.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 148c2ecf20Sopenharmony_ci#include <linux/v4l2-mediabus.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/property.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <media/v4l2-async.h> 198c2ecf20Sopenharmony_ci#include <media/v4l2-clk.h> 208c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 218c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 228c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 238c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 248c2ecf20Sopenharmony_ci#include <media/v4l2-fwnode.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* 278c2ecf20Sopenharmony_ci * MT9M111, MT9M112 and MT9M131: 288c2ecf20Sopenharmony_ci * i2c address is 0x48 or 0x5d (depending on SADDR pin) 298c2ecf20Sopenharmony_ci * The platform has to define struct i2c_board_info objects and link to them 308c2ecf20Sopenharmony_ci * from struct soc_camera_host_desc 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* 348c2ecf20Sopenharmony_ci * Sensor core register addresses (0x000..0x0ff) 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci#define MT9M111_CHIP_VERSION 0x000 378c2ecf20Sopenharmony_ci#define MT9M111_ROW_START 0x001 388c2ecf20Sopenharmony_ci#define MT9M111_COLUMN_START 0x002 398c2ecf20Sopenharmony_ci#define MT9M111_WINDOW_HEIGHT 0x003 408c2ecf20Sopenharmony_ci#define MT9M111_WINDOW_WIDTH 0x004 418c2ecf20Sopenharmony_ci#define MT9M111_HORIZONTAL_BLANKING_B 0x005 428c2ecf20Sopenharmony_ci#define MT9M111_VERTICAL_BLANKING_B 0x006 438c2ecf20Sopenharmony_ci#define MT9M111_HORIZONTAL_BLANKING_A 0x007 448c2ecf20Sopenharmony_ci#define MT9M111_VERTICAL_BLANKING_A 0x008 458c2ecf20Sopenharmony_ci#define MT9M111_SHUTTER_WIDTH 0x009 468c2ecf20Sopenharmony_ci#define MT9M111_ROW_SPEED 0x00a 478c2ecf20Sopenharmony_ci#define MT9M111_EXTRA_DELAY 0x00b 488c2ecf20Sopenharmony_ci#define MT9M111_SHUTTER_DELAY 0x00c 498c2ecf20Sopenharmony_ci#define MT9M111_RESET 0x00d 508c2ecf20Sopenharmony_ci#define MT9M111_READ_MODE_B 0x020 518c2ecf20Sopenharmony_ci#define MT9M111_READ_MODE_A 0x021 528c2ecf20Sopenharmony_ci#define MT9M111_FLASH_CONTROL 0x023 538c2ecf20Sopenharmony_ci#define MT9M111_GREEN1_GAIN 0x02b 548c2ecf20Sopenharmony_ci#define MT9M111_BLUE_GAIN 0x02c 558c2ecf20Sopenharmony_ci#define MT9M111_RED_GAIN 0x02d 568c2ecf20Sopenharmony_ci#define MT9M111_GREEN2_GAIN 0x02e 578c2ecf20Sopenharmony_ci#define MT9M111_GLOBAL_GAIN 0x02f 588c2ecf20Sopenharmony_ci#define MT9M111_CONTEXT_CONTROL 0x0c8 598c2ecf20Sopenharmony_ci#define MT9M111_PAGE_MAP 0x0f0 608c2ecf20Sopenharmony_ci#define MT9M111_BYTE_WISE_ADDR 0x0f1 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define MT9M111_RESET_SYNC_CHANGES (1 << 15) 638c2ecf20Sopenharmony_ci#define MT9M111_RESET_RESTART_BAD_FRAME (1 << 9) 648c2ecf20Sopenharmony_ci#define MT9M111_RESET_SHOW_BAD_FRAMES (1 << 8) 658c2ecf20Sopenharmony_ci#define MT9M111_RESET_RESET_SOC (1 << 5) 668c2ecf20Sopenharmony_ci#define MT9M111_RESET_OUTPUT_DISABLE (1 << 4) 678c2ecf20Sopenharmony_ci#define MT9M111_RESET_CHIP_ENABLE (1 << 3) 688c2ecf20Sopenharmony_ci#define MT9M111_RESET_ANALOG_STANDBY (1 << 2) 698c2ecf20Sopenharmony_ci#define MT9M111_RESET_RESTART_FRAME (1 << 1) 708c2ecf20Sopenharmony_ci#define MT9M111_RESET_RESET_MODE (1 << 0) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#define MT9M111_RM_FULL_POWER_RD (0 << 10) 738c2ecf20Sopenharmony_ci#define MT9M111_RM_LOW_POWER_RD (1 << 10) 748c2ecf20Sopenharmony_ci#define MT9M111_RM_COL_SKIP_4X (1 << 5) 758c2ecf20Sopenharmony_ci#define MT9M111_RM_ROW_SKIP_4X (1 << 4) 768c2ecf20Sopenharmony_ci#define MT9M111_RM_COL_SKIP_2X (1 << 3) 778c2ecf20Sopenharmony_ci#define MT9M111_RM_ROW_SKIP_2X (1 << 2) 788c2ecf20Sopenharmony_ci#define MT9M111_RMB_MIRROR_COLS (1 << 1) 798c2ecf20Sopenharmony_ci#define MT9M111_RMB_MIRROR_ROWS (1 << 0) 808c2ecf20Sopenharmony_ci#define MT9M111_CTXT_CTRL_RESTART (1 << 15) 818c2ecf20Sopenharmony_ci#define MT9M111_CTXT_CTRL_DEFECTCOR_B (1 << 12) 828c2ecf20Sopenharmony_ci#define MT9M111_CTXT_CTRL_RESIZE_B (1 << 10) 838c2ecf20Sopenharmony_ci#define MT9M111_CTXT_CTRL_CTRL2_B (1 << 9) 848c2ecf20Sopenharmony_ci#define MT9M111_CTXT_CTRL_GAMMA_B (1 << 8) 858c2ecf20Sopenharmony_ci#define MT9M111_CTXT_CTRL_XENON_EN (1 << 7) 868c2ecf20Sopenharmony_ci#define MT9M111_CTXT_CTRL_READ_MODE_B (1 << 3) 878c2ecf20Sopenharmony_ci#define MT9M111_CTXT_CTRL_LED_FLASH_EN (1 << 2) 888c2ecf20Sopenharmony_ci#define MT9M111_CTXT_CTRL_VBLANK_SEL_B (1 << 1) 898c2ecf20Sopenharmony_ci#define MT9M111_CTXT_CTRL_HBLANK_SEL_B (1 << 0) 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/* 928c2ecf20Sopenharmony_ci * Colorpipe register addresses (0x100..0x1ff) 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_ci#define MT9M111_OPER_MODE_CTRL 0x106 958c2ecf20Sopenharmony_ci#define MT9M111_OUTPUT_FORMAT_CTRL 0x108 968c2ecf20Sopenharmony_ci#define MT9M111_TPG_CTRL 0x148 978c2ecf20Sopenharmony_ci#define MT9M111_REDUCER_XZOOM_B 0x1a0 988c2ecf20Sopenharmony_ci#define MT9M111_REDUCER_XSIZE_B 0x1a1 998c2ecf20Sopenharmony_ci#define MT9M111_REDUCER_YZOOM_B 0x1a3 1008c2ecf20Sopenharmony_ci#define MT9M111_REDUCER_YSIZE_B 0x1a4 1018c2ecf20Sopenharmony_ci#define MT9M111_REDUCER_XZOOM_A 0x1a6 1028c2ecf20Sopenharmony_ci#define MT9M111_REDUCER_XSIZE_A 0x1a7 1038c2ecf20Sopenharmony_ci#define MT9M111_REDUCER_YZOOM_A 0x1a9 1048c2ecf20Sopenharmony_ci#define MT9M111_REDUCER_YSIZE_A 0x1aa 1058c2ecf20Sopenharmony_ci#define MT9M111_EFFECTS_MODE 0x1e2 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#define MT9M111_OUTPUT_FORMAT_CTRL2_A 0x13a 1088c2ecf20Sopenharmony_ci#define MT9M111_OUTPUT_FORMAT_CTRL2_B 0x19b 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci#define MT9M111_OPMODE_AUTOEXPO_EN (1 << 14) 1118c2ecf20Sopenharmony_ci#define MT9M111_OPMODE_AUTOWHITEBAL_EN (1 << 1) 1128c2ecf20Sopenharmony_ci#define MT9M111_OUTFMT_FLIP_BAYER_COL (1 << 9) 1138c2ecf20Sopenharmony_ci#define MT9M111_OUTFMT_FLIP_BAYER_ROW (1 << 8) 1148c2ecf20Sopenharmony_ci#define MT9M111_OUTFMT_PROCESSED_BAYER (1 << 14) 1158c2ecf20Sopenharmony_ci#define MT9M111_OUTFMT_BYPASS_IFP (1 << 10) 1168c2ecf20Sopenharmony_ci#define MT9M111_OUTFMT_INV_PIX_CLOCK (1 << 9) 1178c2ecf20Sopenharmony_ci#define MT9M111_OUTFMT_RGB (1 << 8) 1188c2ecf20Sopenharmony_ci#define MT9M111_OUTFMT_RGB565 (0 << 6) 1198c2ecf20Sopenharmony_ci#define MT9M111_OUTFMT_RGB555 (1 << 6) 1208c2ecf20Sopenharmony_ci#define MT9M111_OUTFMT_RGB444x (2 << 6) 1218c2ecf20Sopenharmony_ci#define MT9M111_OUTFMT_RGBx444 (3 << 6) 1228c2ecf20Sopenharmony_ci#define MT9M111_OUTFMT_TST_RAMP_OFF (0 << 4) 1238c2ecf20Sopenharmony_ci#define MT9M111_OUTFMT_TST_RAMP_COL (1 << 4) 1248c2ecf20Sopenharmony_ci#define MT9M111_OUTFMT_TST_RAMP_ROW (2 << 4) 1258c2ecf20Sopenharmony_ci#define MT9M111_OUTFMT_TST_RAMP_FRAME (3 << 4) 1268c2ecf20Sopenharmony_ci#define MT9M111_OUTFMT_SHIFT_3_UP (1 << 3) 1278c2ecf20Sopenharmony_ci#define MT9M111_OUTFMT_AVG_CHROMA (1 << 2) 1288c2ecf20Sopenharmony_ci#define MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN (1 << 1) 1298c2ecf20Sopenharmony_ci#define MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B (1 << 0) 1308c2ecf20Sopenharmony_ci#define MT9M111_TPG_SEL_MASK GENMASK(2, 0) 1318c2ecf20Sopenharmony_ci#define MT9M111_EFFECTS_MODE_MASK GENMASK(2, 0) 1328c2ecf20Sopenharmony_ci#define MT9M111_RM_PWR_MASK BIT(10) 1338c2ecf20Sopenharmony_ci#define MT9M111_RM_SKIP2_MASK GENMASK(3, 2) 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* 1368c2ecf20Sopenharmony_ci * Camera control register addresses (0x200..0x2ff not implemented) 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci#define reg_read(reg) mt9m111_reg_read(client, MT9M111_##reg) 1408c2ecf20Sopenharmony_ci#define reg_write(reg, val) mt9m111_reg_write(client, MT9M111_##reg, (val)) 1418c2ecf20Sopenharmony_ci#define reg_set(reg, val) mt9m111_reg_set(client, MT9M111_##reg, (val)) 1428c2ecf20Sopenharmony_ci#define reg_clear(reg, val) mt9m111_reg_clear(client, MT9M111_##reg, (val)) 1438c2ecf20Sopenharmony_ci#define reg_mask(reg, val, mask) mt9m111_reg_mask(client, MT9M111_##reg, \ 1448c2ecf20Sopenharmony_ci (val), (mask)) 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci#define MT9M111_MIN_DARK_ROWS 8 1478c2ecf20Sopenharmony_ci#define MT9M111_MIN_DARK_COLS 26 1488c2ecf20Sopenharmony_ci#define MT9M111_MAX_HEIGHT 1024 1498c2ecf20Sopenharmony_ci#define MT9M111_MAX_WIDTH 1280 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistruct mt9m111_context { 1528c2ecf20Sopenharmony_ci u16 read_mode; 1538c2ecf20Sopenharmony_ci u16 blanking_h; 1548c2ecf20Sopenharmony_ci u16 blanking_v; 1558c2ecf20Sopenharmony_ci u16 reducer_xzoom; 1568c2ecf20Sopenharmony_ci u16 reducer_yzoom; 1578c2ecf20Sopenharmony_ci u16 reducer_xsize; 1588c2ecf20Sopenharmony_ci u16 reducer_ysize; 1598c2ecf20Sopenharmony_ci u16 output_fmt_ctrl2; 1608c2ecf20Sopenharmony_ci u16 control; 1618c2ecf20Sopenharmony_ci}; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic struct mt9m111_context context_a = { 1648c2ecf20Sopenharmony_ci .read_mode = MT9M111_READ_MODE_A, 1658c2ecf20Sopenharmony_ci .blanking_h = MT9M111_HORIZONTAL_BLANKING_A, 1668c2ecf20Sopenharmony_ci .blanking_v = MT9M111_VERTICAL_BLANKING_A, 1678c2ecf20Sopenharmony_ci .reducer_xzoom = MT9M111_REDUCER_XZOOM_A, 1688c2ecf20Sopenharmony_ci .reducer_yzoom = MT9M111_REDUCER_YZOOM_A, 1698c2ecf20Sopenharmony_ci .reducer_xsize = MT9M111_REDUCER_XSIZE_A, 1708c2ecf20Sopenharmony_ci .reducer_ysize = MT9M111_REDUCER_YSIZE_A, 1718c2ecf20Sopenharmony_ci .output_fmt_ctrl2 = MT9M111_OUTPUT_FORMAT_CTRL2_A, 1728c2ecf20Sopenharmony_ci .control = MT9M111_CTXT_CTRL_RESTART, 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic struct mt9m111_context context_b = { 1768c2ecf20Sopenharmony_ci .read_mode = MT9M111_READ_MODE_B, 1778c2ecf20Sopenharmony_ci .blanking_h = MT9M111_HORIZONTAL_BLANKING_B, 1788c2ecf20Sopenharmony_ci .blanking_v = MT9M111_VERTICAL_BLANKING_B, 1798c2ecf20Sopenharmony_ci .reducer_xzoom = MT9M111_REDUCER_XZOOM_B, 1808c2ecf20Sopenharmony_ci .reducer_yzoom = MT9M111_REDUCER_YZOOM_B, 1818c2ecf20Sopenharmony_ci .reducer_xsize = MT9M111_REDUCER_XSIZE_B, 1828c2ecf20Sopenharmony_ci .reducer_ysize = MT9M111_REDUCER_YSIZE_B, 1838c2ecf20Sopenharmony_ci .output_fmt_ctrl2 = MT9M111_OUTPUT_FORMAT_CTRL2_B, 1848c2ecf20Sopenharmony_ci .control = MT9M111_CTXT_CTRL_RESTART | 1858c2ecf20Sopenharmony_ci MT9M111_CTXT_CTRL_DEFECTCOR_B | MT9M111_CTXT_CTRL_RESIZE_B | 1868c2ecf20Sopenharmony_ci MT9M111_CTXT_CTRL_CTRL2_B | MT9M111_CTXT_CTRL_GAMMA_B | 1878c2ecf20Sopenharmony_ci MT9M111_CTXT_CTRL_READ_MODE_B | MT9M111_CTXT_CTRL_VBLANK_SEL_B | 1888c2ecf20Sopenharmony_ci MT9M111_CTXT_CTRL_HBLANK_SEL_B, 1898c2ecf20Sopenharmony_ci}; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci/* MT9M111 has only one fixed colorspace per pixelcode */ 1928c2ecf20Sopenharmony_cistruct mt9m111_datafmt { 1938c2ecf20Sopenharmony_ci u32 code; 1948c2ecf20Sopenharmony_ci enum v4l2_colorspace colorspace; 1958c2ecf20Sopenharmony_ci}; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic const struct mt9m111_datafmt mt9m111_colour_fmts[] = { 1988c2ecf20Sopenharmony_ci {MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_SRGB}, 1998c2ecf20Sopenharmony_ci {MEDIA_BUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_SRGB}, 2008c2ecf20Sopenharmony_ci {MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_SRGB}, 2018c2ecf20Sopenharmony_ci {MEDIA_BUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_SRGB}, 2028c2ecf20Sopenharmony_ci {MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB}, 2038c2ecf20Sopenharmony_ci {MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB}, 2048c2ecf20Sopenharmony_ci {MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB}, 2058c2ecf20Sopenharmony_ci {MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB}, 2068c2ecf20Sopenharmony_ci {MEDIA_BUS_FMT_BGR565_2X8_LE, V4L2_COLORSPACE_SRGB}, 2078c2ecf20Sopenharmony_ci {MEDIA_BUS_FMT_BGR565_2X8_BE, V4L2_COLORSPACE_SRGB}, 2088c2ecf20Sopenharmony_ci {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB}, 2098c2ecf20Sopenharmony_ci {MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB}, 2108c2ecf20Sopenharmony_ci}; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cienum mt9m111_mode_id { 2138c2ecf20Sopenharmony_ci MT9M111_MODE_SXGA_8FPS, 2148c2ecf20Sopenharmony_ci MT9M111_MODE_SXGA_15FPS, 2158c2ecf20Sopenharmony_ci MT9M111_MODE_QSXGA_30FPS, 2168c2ecf20Sopenharmony_ci MT9M111_NUM_MODES, 2178c2ecf20Sopenharmony_ci}; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistruct mt9m111_mode_info { 2208c2ecf20Sopenharmony_ci unsigned int sensor_w; 2218c2ecf20Sopenharmony_ci unsigned int sensor_h; 2228c2ecf20Sopenharmony_ci unsigned int max_image_w; 2238c2ecf20Sopenharmony_ci unsigned int max_image_h; 2248c2ecf20Sopenharmony_ci unsigned int max_fps; 2258c2ecf20Sopenharmony_ci unsigned int reg_val; 2268c2ecf20Sopenharmony_ci unsigned int reg_mask; 2278c2ecf20Sopenharmony_ci}; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistruct mt9m111 { 2308c2ecf20Sopenharmony_ci struct v4l2_subdev subdev; 2318c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler hdl; 2328c2ecf20Sopenharmony_ci struct v4l2_ctrl *gain; 2338c2ecf20Sopenharmony_ci struct mt9m111_context *ctx; 2348c2ecf20Sopenharmony_ci struct v4l2_rect rect; /* cropping rectangle */ 2358c2ecf20Sopenharmony_ci struct v4l2_clk *clk; 2368c2ecf20Sopenharmony_ci unsigned int width; /* output */ 2378c2ecf20Sopenharmony_ci unsigned int height; /* sizes */ 2388c2ecf20Sopenharmony_ci struct v4l2_fract frame_interval; 2398c2ecf20Sopenharmony_ci const struct mt9m111_mode_info *current_mode; 2408c2ecf20Sopenharmony_ci struct mutex power_lock; /* lock to protect power_count */ 2418c2ecf20Sopenharmony_ci int power_count; 2428c2ecf20Sopenharmony_ci const struct mt9m111_datafmt *fmt; 2438c2ecf20Sopenharmony_ci int lastpage; /* PageMap cache value */ 2448c2ecf20Sopenharmony_ci struct regulator *regulator; 2458c2ecf20Sopenharmony_ci bool is_streaming; 2468c2ecf20Sopenharmony_ci /* user point of view - 0: falling 1: rising edge */ 2478c2ecf20Sopenharmony_ci unsigned int pclk_sample:1; 2488c2ecf20Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER 2498c2ecf20Sopenharmony_ci struct media_pad pad; 2508c2ecf20Sopenharmony_ci#endif 2518c2ecf20Sopenharmony_ci}; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic const struct mt9m111_mode_info mt9m111_mode_data[MT9M111_NUM_MODES] = { 2548c2ecf20Sopenharmony_ci [MT9M111_MODE_SXGA_8FPS] = { 2558c2ecf20Sopenharmony_ci .sensor_w = 1280, 2568c2ecf20Sopenharmony_ci .sensor_h = 1024, 2578c2ecf20Sopenharmony_ci .max_image_w = 1280, 2588c2ecf20Sopenharmony_ci .max_image_h = 1024, 2598c2ecf20Sopenharmony_ci .max_fps = 8, 2608c2ecf20Sopenharmony_ci .reg_val = MT9M111_RM_LOW_POWER_RD, 2618c2ecf20Sopenharmony_ci .reg_mask = MT9M111_RM_PWR_MASK | MT9M111_RM_SKIP2_MASK, 2628c2ecf20Sopenharmony_ci }, 2638c2ecf20Sopenharmony_ci [MT9M111_MODE_SXGA_15FPS] = { 2648c2ecf20Sopenharmony_ci .sensor_w = 1280, 2658c2ecf20Sopenharmony_ci .sensor_h = 1024, 2668c2ecf20Sopenharmony_ci .max_image_w = 1280, 2678c2ecf20Sopenharmony_ci .max_image_h = 1024, 2688c2ecf20Sopenharmony_ci .max_fps = 15, 2698c2ecf20Sopenharmony_ci .reg_val = MT9M111_RM_FULL_POWER_RD, 2708c2ecf20Sopenharmony_ci .reg_mask = MT9M111_RM_PWR_MASK | MT9M111_RM_SKIP2_MASK, 2718c2ecf20Sopenharmony_ci }, 2728c2ecf20Sopenharmony_ci [MT9M111_MODE_QSXGA_30FPS] = { 2738c2ecf20Sopenharmony_ci .sensor_w = 1280, 2748c2ecf20Sopenharmony_ci .sensor_h = 1024, 2758c2ecf20Sopenharmony_ci .max_image_w = 640, 2768c2ecf20Sopenharmony_ci .max_image_h = 512, 2778c2ecf20Sopenharmony_ci .max_fps = 30, 2788c2ecf20Sopenharmony_ci .reg_val = MT9M111_RM_LOW_POWER_RD | MT9M111_RM_COL_SKIP_2X | 2798c2ecf20Sopenharmony_ci MT9M111_RM_ROW_SKIP_2X, 2808c2ecf20Sopenharmony_ci .reg_mask = MT9M111_RM_PWR_MASK | MT9M111_RM_SKIP2_MASK, 2818c2ecf20Sopenharmony_ci }, 2828c2ecf20Sopenharmony_ci}; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci/* Find a data format by a pixel code */ 2858c2ecf20Sopenharmony_cistatic const struct mt9m111_datafmt *mt9m111_find_datafmt(struct mt9m111 *mt9m111, 2868c2ecf20Sopenharmony_ci u32 code) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci int i; 2898c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mt9m111_colour_fmts); i++) 2908c2ecf20Sopenharmony_ci if (mt9m111_colour_fmts[i].code == code) 2918c2ecf20Sopenharmony_ci return mt9m111_colour_fmts + i; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci return mt9m111->fmt; 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic struct mt9m111 *to_mt9m111(const struct i2c_client *client) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci return container_of(i2c_get_clientdata(client), struct mt9m111, subdev); 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic int reg_page_map_set(struct i2c_client *client, const u16 reg) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci int ret; 3048c2ecf20Sopenharmony_ci u16 page; 3058c2ecf20Sopenharmony_ci struct mt9m111 *mt9m111 = to_mt9m111(client); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci page = (reg >> 8); 3088c2ecf20Sopenharmony_ci if (page == mt9m111->lastpage) 3098c2ecf20Sopenharmony_ci return 0; 3108c2ecf20Sopenharmony_ci if (page > 2) 3118c2ecf20Sopenharmony_ci return -EINVAL; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci ret = i2c_smbus_write_word_swapped(client, MT9M111_PAGE_MAP, page); 3148c2ecf20Sopenharmony_ci if (!ret) 3158c2ecf20Sopenharmony_ci mt9m111->lastpage = page; 3168c2ecf20Sopenharmony_ci return ret; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic int mt9m111_reg_read(struct i2c_client *client, const u16 reg) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci int ret; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci ret = reg_page_map_set(client, reg); 3248c2ecf20Sopenharmony_ci if (!ret) 3258c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_swapped(client, reg & 0xff); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "read reg.%03x -> %04x\n", reg, ret); 3288c2ecf20Sopenharmony_ci return ret; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int mt9m111_reg_write(struct i2c_client *client, const u16 reg, 3328c2ecf20Sopenharmony_ci const u16 data) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci int ret; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci ret = reg_page_map_set(client, reg); 3378c2ecf20Sopenharmony_ci if (!ret) 3388c2ecf20Sopenharmony_ci ret = i2c_smbus_write_word_swapped(client, reg & 0xff, data); 3398c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "write reg.%03x = %04x -> %d\n", reg, data, ret); 3408c2ecf20Sopenharmony_ci return ret; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic int mt9m111_reg_set(struct i2c_client *client, const u16 reg, 3448c2ecf20Sopenharmony_ci const u16 data) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci int ret; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci ret = mt9m111_reg_read(client, reg); 3498c2ecf20Sopenharmony_ci if (ret >= 0) 3508c2ecf20Sopenharmony_ci ret = mt9m111_reg_write(client, reg, ret | data); 3518c2ecf20Sopenharmony_ci return ret; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic int mt9m111_reg_clear(struct i2c_client *client, const u16 reg, 3558c2ecf20Sopenharmony_ci const u16 data) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci int ret; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci ret = mt9m111_reg_read(client, reg); 3608c2ecf20Sopenharmony_ci if (ret >= 0) 3618c2ecf20Sopenharmony_ci ret = mt9m111_reg_write(client, reg, ret & ~data); 3628c2ecf20Sopenharmony_ci return ret; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic int mt9m111_reg_mask(struct i2c_client *client, const u16 reg, 3668c2ecf20Sopenharmony_ci const u16 data, const u16 mask) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci int ret; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci ret = mt9m111_reg_read(client, reg); 3718c2ecf20Sopenharmony_ci if (ret >= 0) 3728c2ecf20Sopenharmony_ci ret = mt9m111_reg_write(client, reg, (ret & ~mask) | data); 3738c2ecf20Sopenharmony_ci return ret; 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic int mt9m111_set_context(struct mt9m111 *mt9m111, 3778c2ecf20Sopenharmony_ci struct mt9m111_context *ctx) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); 3808c2ecf20Sopenharmony_ci return reg_write(CONTEXT_CONTROL, ctx->control); 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic int mt9m111_setup_rect_ctx(struct mt9m111 *mt9m111, 3848c2ecf20Sopenharmony_ci struct mt9m111_context *ctx, struct v4l2_rect *rect, 3858c2ecf20Sopenharmony_ci unsigned int width, unsigned int height) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); 3888c2ecf20Sopenharmony_ci int ret = mt9m111_reg_write(client, ctx->reducer_xzoom, rect->width); 3898c2ecf20Sopenharmony_ci if (!ret) 3908c2ecf20Sopenharmony_ci ret = mt9m111_reg_write(client, ctx->reducer_yzoom, rect->height); 3918c2ecf20Sopenharmony_ci if (!ret) 3928c2ecf20Sopenharmony_ci ret = mt9m111_reg_write(client, ctx->reducer_xsize, width); 3938c2ecf20Sopenharmony_ci if (!ret) 3948c2ecf20Sopenharmony_ci ret = mt9m111_reg_write(client, ctx->reducer_ysize, height); 3958c2ecf20Sopenharmony_ci return ret; 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic int mt9m111_setup_geometry(struct mt9m111 *mt9m111, struct v4l2_rect *rect, 3998c2ecf20Sopenharmony_ci int width, int height, u32 code) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); 4028c2ecf20Sopenharmony_ci int ret; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci ret = reg_write(COLUMN_START, rect->left); 4058c2ecf20Sopenharmony_ci if (!ret) 4068c2ecf20Sopenharmony_ci ret = reg_write(ROW_START, rect->top); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (!ret) 4098c2ecf20Sopenharmony_ci ret = reg_write(WINDOW_WIDTH, rect->width); 4108c2ecf20Sopenharmony_ci if (!ret) 4118c2ecf20Sopenharmony_ci ret = reg_write(WINDOW_HEIGHT, rect->height); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (code != MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE) { 4148c2ecf20Sopenharmony_ci /* IFP in use, down-scaling possible */ 4158c2ecf20Sopenharmony_ci if (!ret) 4168c2ecf20Sopenharmony_ci ret = mt9m111_setup_rect_ctx(mt9m111, &context_b, 4178c2ecf20Sopenharmony_ci rect, width, height); 4188c2ecf20Sopenharmony_ci if (!ret) 4198c2ecf20Sopenharmony_ci ret = mt9m111_setup_rect_ctx(mt9m111, &context_a, 4208c2ecf20Sopenharmony_ci rect, width, height); 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "%s(%x): %ux%u@%u:%u -> %ux%u = %d\n", 4248c2ecf20Sopenharmony_ci __func__, code, rect->width, rect->height, rect->left, rect->top, 4258c2ecf20Sopenharmony_ci width, height, ret); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci return ret; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic int mt9m111_enable(struct mt9m111 *mt9m111) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); 4338c2ecf20Sopenharmony_ci return reg_write(RESET, MT9M111_RESET_CHIP_ENABLE); 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic int mt9m111_reset(struct mt9m111 *mt9m111) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); 4398c2ecf20Sopenharmony_ci int ret; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci ret = reg_set(RESET, MT9M111_RESET_RESET_MODE); 4428c2ecf20Sopenharmony_ci if (!ret) 4438c2ecf20Sopenharmony_ci ret = reg_set(RESET, MT9M111_RESET_RESET_SOC); 4448c2ecf20Sopenharmony_ci if (!ret) 4458c2ecf20Sopenharmony_ci ret = reg_clear(RESET, MT9M111_RESET_RESET_MODE 4468c2ecf20Sopenharmony_ci | MT9M111_RESET_RESET_SOC); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci return ret; 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic int mt9m111_set_selection(struct v4l2_subdev *sd, 4528c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 4538c2ecf20Sopenharmony_ci struct v4l2_subdev_selection *sel) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 4568c2ecf20Sopenharmony_ci struct mt9m111 *mt9m111 = to_mt9m111(client); 4578c2ecf20Sopenharmony_ci struct v4l2_rect rect = sel->r; 4588c2ecf20Sopenharmony_ci int width, height; 4598c2ecf20Sopenharmony_ci int ret, align = 0; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || 4628c2ecf20Sopenharmony_ci sel->target != V4L2_SEL_TGT_CROP) 4638c2ecf20Sopenharmony_ci return -EINVAL; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if (mt9m111->fmt->code == MEDIA_BUS_FMT_SBGGR8_1X8 || 4668c2ecf20Sopenharmony_ci mt9m111->fmt->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE) { 4678c2ecf20Sopenharmony_ci /* Bayer format - even size lengths */ 4688c2ecf20Sopenharmony_ci align = 1; 4698c2ecf20Sopenharmony_ci /* Let the user play with the starting pixel */ 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci /* FIXME: the datasheet doesn't specify minimum sizes */ 4738c2ecf20Sopenharmony_ci v4l_bound_align_image(&rect.width, 2, MT9M111_MAX_WIDTH, align, 4748c2ecf20Sopenharmony_ci &rect.height, 2, MT9M111_MAX_HEIGHT, align, 0); 4758c2ecf20Sopenharmony_ci rect.left = clamp(rect.left, MT9M111_MIN_DARK_COLS, 4768c2ecf20Sopenharmony_ci MT9M111_MIN_DARK_COLS + MT9M111_MAX_WIDTH - 4778c2ecf20Sopenharmony_ci (__s32)rect.width); 4788c2ecf20Sopenharmony_ci rect.top = clamp(rect.top, MT9M111_MIN_DARK_ROWS, 4798c2ecf20Sopenharmony_ci MT9M111_MIN_DARK_ROWS + MT9M111_MAX_HEIGHT - 4808c2ecf20Sopenharmony_ci (__s32)rect.height); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci width = min(mt9m111->width, rect.width); 4838c2ecf20Sopenharmony_ci height = min(mt9m111->height, rect.height); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci ret = mt9m111_setup_geometry(mt9m111, &rect, width, height, mt9m111->fmt->code); 4868c2ecf20Sopenharmony_ci if (!ret) { 4878c2ecf20Sopenharmony_ci mt9m111->rect = rect; 4888c2ecf20Sopenharmony_ci mt9m111->width = width; 4898c2ecf20Sopenharmony_ci mt9m111->height = height; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci return ret; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic int mt9m111_get_selection(struct v4l2_subdev *sd, 4968c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 4978c2ecf20Sopenharmony_ci struct v4l2_subdev_selection *sel) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 5008c2ecf20Sopenharmony_ci struct mt9m111 *mt9m111 = to_mt9m111(client); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) 5038c2ecf20Sopenharmony_ci return -EINVAL; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci switch (sel->target) { 5068c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 5078c2ecf20Sopenharmony_ci sel->r.left = MT9M111_MIN_DARK_COLS; 5088c2ecf20Sopenharmony_ci sel->r.top = MT9M111_MIN_DARK_ROWS; 5098c2ecf20Sopenharmony_ci sel->r.width = MT9M111_MAX_WIDTH; 5108c2ecf20Sopenharmony_ci sel->r.height = MT9M111_MAX_HEIGHT; 5118c2ecf20Sopenharmony_ci return 0; 5128c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP: 5138c2ecf20Sopenharmony_ci sel->r = mt9m111->rect; 5148c2ecf20Sopenharmony_ci return 0; 5158c2ecf20Sopenharmony_ci default: 5168c2ecf20Sopenharmony_ci return -EINVAL; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic int mt9m111_get_fmt(struct v4l2_subdev *sd, 5218c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 5228c2ecf20Sopenharmony_ci struct v4l2_subdev_format *format) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &format->format; 5258c2ecf20Sopenharmony_ci struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci if (format->pad) 5288c2ecf20Sopenharmony_ci return -EINVAL; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (format->which == V4L2_SUBDEV_FORMAT_TRY) { 5318c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API 5328c2ecf20Sopenharmony_ci mf = v4l2_subdev_get_try_format(sd, cfg, format->pad); 5338c2ecf20Sopenharmony_ci format->format = *mf; 5348c2ecf20Sopenharmony_ci return 0; 5358c2ecf20Sopenharmony_ci#else 5368c2ecf20Sopenharmony_ci return -EINVAL; 5378c2ecf20Sopenharmony_ci#endif 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci mf->width = mt9m111->width; 5418c2ecf20Sopenharmony_ci mf->height = mt9m111->height; 5428c2ecf20Sopenharmony_ci mf->code = mt9m111->fmt->code; 5438c2ecf20Sopenharmony_ci mf->colorspace = mt9m111->fmt->colorspace; 5448c2ecf20Sopenharmony_ci mf->field = V4L2_FIELD_NONE; 5458c2ecf20Sopenharmony_ci mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 5468c2ecf20Sopenharmony_ci mf->quantization = V4L2_QUANTIZATION_DEFAULT; 5478c2ecf20Sopenharmony_ci mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci return 0; 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic int mt9m111_set_pixfmt(struct mt9m111 *mt9m111, 5538c2ecf20Sopenharmony_ci u32 code) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); 5568c2ecf20Sopenharmony_ci u16 data_outfmt2, mask_outfmt2 = MT9M111_OUTFMT_PROCESSED_BAYER | 5578c2ecf20Sopenharmony_ci MT9M111_OUTFMT_BYPASS_IFP | MT9M111_OUTFMT_RGB | 5588c2ecf20Sopenharmony_ci MT9M111_OUTFMT_RGB565 | MT9M111_OUTFMT_RGB555 | 5598c2ecf20Sopenharmony_ci MT9M111_OUTFMT_RGB444x | MT9M111_OUTFMT_RGBx444 | 5608c2ecf20Sopenharmony_ci MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN | 5618c2ecf20Sopenharmony_ci MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B; 5628c2ecf20Sopenharmony_ci int ret; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci switch (code) { 5658c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR8_1X8: 5668c2ecf20Sopenharmony_ci data_outfmt2 = MT9M111_OUTFMT_PROCESSED_BAYER | 5678c2ecf20Sopenharmony_ci MT9M111_OUTFMT_RGB; 5688c2ecf20Sopenharmony_ci break; 5698c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE: 5708c2ecf20Sopenharmony_ci data_outfmt2 = MT9M111_OUTFMT_BYPASS_IFP | MT9M111_OUTFMT_RGB; 5718c2ecf20Sopenharmony_ci break; 5728c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: 5738c2ecf20Sopenharmony_ci data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555 | 5748c2ecf20Sopenharmony_ci MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN; 5758c2ecf20Sopenharmony_ci break; 5768c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE: 5778c2ecf20Sopenharmony_ci data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555; 5788c2ecf20Sopenharmony_ci break; 5798c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB565_2X8_LE: 5808c2ecf20Sopenharmony_ci data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 | 5818c2ecf20Sopenharmony_ci MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN; 5828c2ecf20Sopenharmony_ci break; 5838c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB565_2X8_BE: 5848c2ecf20Sopenharmony_ci data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565; 5858c2ecf20Sopenharmony_ci break; 5868c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_BGR565_2X8_BE: 5878c2ecf20Sopenharmony_ci data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 | 5888c2ecf20Sopenharmony_ci MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B; 5898c2ecf20Sopenharmony_ci break; 5908c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_BGR565_2X8_LE: 5918c2ecf20Sopenharmony_ci data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 | 5928c2ecf20Sopenharmony_ci MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN | 5938c2ecf20Sopenharmony_ci MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B; 5948c2ecf20Sopenharmony_ci break; 5958c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_2X8: 5968c2ecf20Sopenharmony_ci data_outfmt2 = 0; 5978c2ecf20Sopenharmony_ci break; 5988c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_VYUY8_2X8: 5998c2ecf20Sopenharmony_ci data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B; 6008c2ecf20Sopenharmony_ci break; 6018c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_YUYV8_2X8: 6028c2ecf20Sopenharmony_ci data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN; 6038c2ecf20Sopenharmony_ci break; 6048c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_YVYU8_2X8: 6058c2ecf20Sopenharmony_ci data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN | 6068c2ecf20Sopenharmony_ci MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B; 6078c2ecf20Sopenharmony_ci break; 6088c2ecf20Sopenharmony_ci default: 6098c2ecf20Sopenharmony_ci dev_err(&client->dev, "Pixel format not handled: %x\n", code); 6108c2ecf20Sopenharmony_ci return -EINVAL; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci /* receiver samples on falling edge, chip-hw default is rising */ 6148c2ecf20Sopenharmony_ci if (mt9m111->pclk_sample == 0) 6158c2ecf20Sopenharmony_ci mask_outfmt2 |= MT9M111_OUTFMT_INV_PIX_CLOCK; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci ret = mt9m111_reg_mask(client, context_a.output_fmt_ctrl2, 6188c2ecf20Sopenharmony_ci data_outfmt2, mask_outfmt2); 6198c2ecf20Sopenharmony_ci if (!ret) 6208c2ecf20Sopenharmony_ci ret = mt9m111_reg_mask(client, context_b.output_fmt_ctrl2, 6218c2ecf20Sopenharmony_ci data_outfmt2, mask_outfmt2); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci return ret; 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cistatic int mt9m111_set_fmt(struct v4l2_subdev *sd, 6278c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 6288c2ecf20Sopenharmony_ci struct v4l2_subdev_format *format) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &format->format; 6318c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 6328c2ecf20Sopenharmony_ci struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); 6338c2ecf20Sopenharmony_ci const struct mt9m111_datafmt *fmt; 6348c2ecf20Sopenharmony_ci struct v4l2_rect *rect = &mt9m111->rect; 6358c2ecf20Sopenharmony_ci bool bayer; 6368c2ecf20Sopenharmony_ci int ret; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (mt9m111->is_streaming) 6398c2ecf20Sopenharmony_ci return -EBUSY; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci if (format->pad) 6428c2ecf20Sopenharmony_ci return -EINVAL; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci fmt = mt9m111_find_datafmt(mt9m111, mf->code); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci bayer = fmt->code == MEDIA_BUS_FMT_SBGGR8_1X8 || 6478c2ecf20Sopenharmony_ci fmt->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci /* 6508c2ecf20Sopenharmony_ci * With Bayer format enforce even side lengths, but let the user play 6518c2ecf20Sopenharmony_ci * with the starting pixel 6528c2ecf20Sopenharmony_ci */ 6538c2ecf20Sopenharmony_ci if (bayer) { 6548c2ecf20Sopenharmony_ci rect->width = ALIGN(rect->width, 2); 6558c2ecf20Sopenharmony_ci rect->height = ALIGN(rect->height, 2); 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci if (fmt->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE) { 6598c2ecf20Sopenharmony_ci /* IFP bypass mode, no scaling */ 6608c2ecf20Sopenharmony_ci mf->width = rect->width; 6618c2ecf20Sopenharmony_ci mf->height = rect->height; 6628c2ecf20Sopenharmony_ci } else { 6638c2ecf20Sopenharmony_ci /* No upscaling */ 6648c2ecf20Sopenharmony_ci if (mf->width > rect->width) 6658c2ecf20Sopenharmony_ci mf->width = rect->width; 6668c2ecf20Sopenharmony_ci if (mf->height > rect->height) 6678c2ecf20Sopenharmony_ci mf->height = rect->height; 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "%s(): %ux%u, code=%x\n", __func__, 6718c2ecf20Sopenharmony_ci mf->width, mf->height, fmt->code); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci mf->code = fmt->code; 6748c2ecf20Sopenharmony_ci mf->colorspace = fmt->colorspace; 6758c2ecf20Sopenharmony_ci mf->field = V4L2_FIELD_NONE; 6768c2ecf20Sopenharmony_ci mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 6778c2ecf20Sopenharmony_ci mf->quantization = V4L2_QUANTIZATION_DEFAULT; 6788c2ecf20Sopenharmony_ci mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (format->which == V4L2_SUBDEV_FORMAT_TRY) { 6818c2ecf20Sopenharmony_ci cfg->try_fmt = *mf; 6828c2ecf20Sopenharmony_ci return 0; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci ret = mt9m111_setup_geometry(mt9m111, rect, mf->width, mf->height, mf->code); 6868c2ecf20Sopenharmony_ci if (!ret) 6878c2ecf20Sopenharmony_ci ret = mt9m111_set_pixfmt(mt9m111, mf->code); 6888c2ecf20Sopenharmony_ci if (!ret) { 6898c2ecf20Sopenharmony_ci mt9m111->width = mf->width; 6908c2ecf20Sopenharmony_ci mt9m111->height = mf->height; 6918c2ecf20Sopenharmony_ci mt9m111->fmt = fmt; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci return ret; 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_cistatic const struct mt9m111_mode_info * 6988c2ecf20Sopenharmony_cimt9m111_find_mode(struct mt9m111 *mt9m111, unsigned int req_fps, 6998c2ecf20Sopenharmony_ci unsigned int width, unsigned int height) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci const struct mt9m111_mode_info *mode; 7028c2ecf20Sopenharmony_ci struct v4l2_rect *sensor_rect = &mt9m111->rect; 7038c2ecf20Sopenharmony_ci unsigned int gap, gap_best = (unsigned int) -1; 7048c2ecf20Sopenharmony_ci int i, best_gap_idx = MT9M111_MODE_SXGA_15FPS; 7058c2ecf20Sopenharmony_ci bool skip_30fps = false; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci /* 7088c2ecf20Sopenharmony_ci * The fps selection is based on the row, column skipping mechanism. 7098c2ecf20Sopenharmony_ci * So ensure that the sensor window is set to default else the fps 7108c2ecf20Sopenharmony_ci * aren't calculated correctly within the sensor hw. 7118c2ecf20Sopenharmony_ci */ 7128c2ecf20Sopenharmony_ci if (sensor_rect->width != MT9M111_MAX_WIDTH || 7138c2ecf20Sopenharmony_ci sensor_rect->height != MT9M111_MAX_HEIGHT) { 7148c2ecf20Sopenharmony_ci dev_info(mt9m111->subdev.dev, 7158c2ecf20Sopenharmony_ci "Framerate selection is not supported for cropped " 7168c2ecf20Sopenharmony_ci "images\n"); 7178c2ecf20Sopenharmony_ci return NULL; 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci /* 30fps only supported for images not exceeding 640x512 */ 7218c2ecf20Sopenharmony_ci if (width > MT9M111_MAX_WIDTH / 2 || height > MT9M111_MAX_HEIGHT / 2) { 7228c2ecf20Sopenharmony_ci dev_dbg(mt9m111->subdev.dev, 7238c2ecf20Sopenharmony_ci "Framerates > 15fps are supported only for images " 7248c2ecf20Sopenharmony_ci "not exceeding 640x512\n"); 7258c2ecf20Sopenharmony_ci skip_30fps = true; 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci /* find best matched fps */ 7298c2ecf20Sopenharmony_ci for (i = 0; i < MT9M111_NUM_MODES; i++) { 7308c2ecf20Sopenharmony_ci unsigned int fps = mt9m111_mode_data[i].max_fps; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci if (fps == 30 && skip_30fps) 7338c2ecf20Sopenharmony_ci continue; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci gap = abs(fps - req_fps); 7368c2ecf20Sopenharmony_ci if (gap < gap_best) { 7378c2ecf20Sopenharmony_ci best_gap_idx = i; 7388c2ecf20Sopenharmony_ci gap_best = gap; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci /* 7438c2ecf20Sopenharmony_ci * Use context a/b default timing values instead of calculate blanking 7448c2ecf20Sopenharmony_ci * timing values. 7458c2ecf20Sopenharmony_ci */ 7468c2ecf20Sopenharmony_ci mode = &mt9m111_mode_data[best_gap_idx]; 7478c2ecf20Sopenharmony_ci mt9m111->ctx = (best_gap_idx == MT9M111_MODE_QSXGA_30FPS) ? &context_a : 7488c2ecf20Sopenharmony_ci &context_b; 7498c2ecf20Sopenharmony_ci return mode; 7508c2ecf20Sopenharmony_ci} 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 7538c2ecf20Sopenharmony_cistatic int mt9m111_g_register(struct v4l2_subdev *sd, 7548c2ecf20Sopenharmony_ci struct v4l2_dbg_register *reg) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 7578c2ecf20Sopenharmony_ci int val; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci if (reg->reg > 0x2ff) 7608c2ecf20Sopenharmony_ci return -EINVAL; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci val = mt9m111_reg_read(client, reg->reg); 7638c2ecf20Sopenharmony_ci reg->size = 2; 7648c2ecf20Sopenharmony_ci reg->val = (u64)val; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci if (reg->val > 0xffff) 7678c2ecf20Sopenharmony_ci return -EIO; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci return 0; 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_cistatic int mt9m111_s_register(struct v4l2_subdev *sd, 7738c2ecf20Sopenharmony_ci const struct v4l2_dbg_register *reg) 7748c2ecf20Sopenharmony_ci{ 7758c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci if (reg->reg > 0x2ff) 7788c2ecf20Sopenharmony_ci return -EINVAL; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci if (mt9m111_reg_write(client, reg->reg, reg->val) < 0) 7818c2ecf20Sopenharmony_ci return -EIO; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci return 0; 7848c2ecf20Sopenharmony_ci} 7858c2ecf20Sopenharmony_ci#endif 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_cistatic int mt9m111_set_flip(struct mt9m111 *mt9m111, int flip, int mask) 7888c2ecf20Sopenharmony_ci{ 7898c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); 7908c2ecf20Sopenharmony_ci int ret; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci if (flip) 7938c2ecf20Sopenharmony_ci ret = mt9m111_reg_set(client, mt9m111->ctx->read_mode, mask); 7948c2ecf20Sopenharmony_ci else 7958c2ecf20Sopenharmony_ci ret = mt9m111_reg_clear(client, mt9m111->ctx->read_mode, mask); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci return ret; 7988c2ecf20Sopenharmony_ci} 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_cistatic int mt9m111_get_global_gain(struct mt9m111 *mt9m111) 8018c2ecf20Sopenharmony_ci{ 8028c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); 8038c2ecf20Sopenharmony_ci int data; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci data = reg_read(GLOBAL_GAIN); 8068c2ecf20Sopenharmony_ci if (data >= 0) 8078c2ecf20Sopenharmony_ci return (data & 0x2f) * (1 << ((data >> 10) & 1)) * 8088c2ecf20Sopenharmony_ci (1 << ((data >> 9) & 1)); 8098c2ecf20Sopenharmony_ci return data; 8108c2ecf20Sopenharmony_ci} 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_cistatic int mt9m111_set_global_gain(struct mt9m111 *mt9m111, int gain) 8138c2ecf20Sopenharmony_ci{ 8148c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); 8158c2ecf20Sopenharmony_ci u16 val; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci if (gain > 63 * 2 * 2) 8188c2ecf20Sopenharmony_ci return -EINVAL; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci if ((gain >= 64 * 2) && (gain < 63 * 2 * 2)) 8218c2ecf20Sopenharmony_ci val = (1 << 10) | (1 << 9) | (gain / 4); 8228c2ecf20Sopenharmony_ci else if ((gain >= 64) && (gain < 64 * 2)) 8238c2ecf20Sopenharmony_ci val = (1 << 9) | (gain / 2); 8248c2ecf20Sopenharmony_ci else 8258c2ecf20Sopenharmony_ci val = gain; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci return reg_write(GLOBAL_GAIN, val); 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_cistatic int mt9m111_set_autoexposure(struct mt9m111 *mt9m111, int val) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci if (val == V4L2_EXPOSURE_AUTO) 8358c2ecf20Sopenharmony_ci return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN); 8368c2ecf20Sopenharmony_ci return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN); 8378c2ecf20Sopenharmony_ci} 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_cistatic int mt9m111_set_autowhitebalance(struct mt9m111 *mt9m111, int on) 8408c2ecf20Sopenharmony_ci{ 8418c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci if (on) 8448c2ecf20Sopenharmony_ci return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN); 8458c2ecf20Sopenharmony_ci return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN); 8468c2ecf20Sopenharmony_ci} 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_cistatic const char * const mt9m111_test_pattern_menu[] = { 8498c2ecf20Sopenharmony_ci "Disabled", 8508c2ecf20Sopenharmony_ci "Vertical monochrome gradient", 8518c2ecf20Sopenharmony_ci "Flat color type 1", 8528c2ecf20Sopenharmony_ci "Flat color type 2", 8538c2ecf20Sopenharmony_ci "Flat color type 3", 8548c2ecf20Sopenharmony_ci "Flat color type 4", 8558c2ecf20Sopenharmony_ci "Flat color type 5", 8568c2ecf20Sopenharmony_ci "Color bar", 8578c2ecf20Sopenharmony_ci}; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_cistatic int mt9m111_set_test_pattern(struct mt9m111 *mt9m111, int val) 8608c2ecf20Sopenharmony_ci{ 8618c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci return mt9m111_reg_mask(client, MT9M111_TPG_CTRL, val, 8648c2ecf20Sopenharmony_ci MT9M111_TPG_SEL_MASK); 8658c2ecf20Sopenharmony_ci} 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_cistatic int mt9m111_set_colorfx(struct mt9m111 *mt9m111, int val) 8688c2ecf20Sopenharmony_ci{ 8698c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); 8708c2ecf20Sopenharmony_ci static const struct v4l2_control colorfx[] = { 8718c2ecf20Sopenharmony_ci { V4L2_COLORFX_NONE, 0 }, 8728c2ecf20Sopenharmony_ci { V4L2_COLORFX_BW, 1 }, 8738c2ecf20Sopenharmony_ci { V4L2_COLORFX_SEPIA, 2 }, 8748c2ecf20Sopenharmony_ci { V4L2_COLORFX_NEGATIVE, 3 }, 8758c2ecf20Sopenharmony_ci { V4L2_COLORFX_SOLARIZATION, 4 }, 8768c2ecf20Sopenharmony_ci }; 8778c2ecf20Sopenharmony_ci int i; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(colorfx); i++) { 8808c2ecf20Sopenharmony_ci if (colorfx[i].id == val) { 8818c2ecf20Sopenharmony_ci return mt9m111_reg_mask(client, MT9M111_EFFECTS_MODE, 8828c2ecf20Sopenharmony_ci colorfx[i].value, 8838c2ecf20Sopenharmony_ci MT9M111_EFFECTS_MODE_MASK); 8848c2ecf20Sopenharmony_ci } 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci return -EINVAL; 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic int mt9m111_s_ctrl(struct v4l2_ctrl *ctrl) 8918c2ecf20Sopenharmony_ci{ 8928c2ecf20Sopenharmony_ci struct mt9m111 *mt9m111 = container_of(ctrl->handler, 8938c2ecf20Sopenharmony_ci struct mt9m111, hdl); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci switch (ctrl->id) { 8968c2ecf20Sopenharmony_ci case V4L2_CID_VFLIP: 8978c2ecf20Sopenharmony_ci return mt9m111_set_flip(mt9m111, ctrl->val, 8988c2ecf20Sopenharmony_ci MT9M111_RMB_MIRROR_ROWS); 8998c2ecf20Sopenharmony_ci case V4L2_CID_HFLIP: 9008c2ecf20Sopenharmony_ci return mt9m111_set_flip(mt9m111, ctrl->val, 9018c2ecf20Sopenharmony_ci MT9M111_RMB_MIRROR_COLS); 9028c2ecf20Sopenharmony_ci case V4L2_CID_GAIN: 9038c2ecf20Sopenharmony_ci return mt9m111_set_global_gain(mt9m111, ctrl->val); 9048c2ecf20Sopenharmony_ci case V4L2_CID_EXPOSURE_AUTO: 9058c2ecf20Sopenharmony_ci return mt9m111_set_autoexposure(mt9m111, ctrl->val); 9068c2ecf20Sopenharmony_ci case V4L2_CID_AUTO_WHITE_BALANCE: 9078c2ecf20Sopenharmony_ci return mt9m111_set_autowhitebalance(mt9m111, ctrl->val); 9088c2ecf20Sopenharmony_ci case V4L2_CID_TEST_PATTERN: 9098c2ecf20Sopenharmony_ci return mt9m111_set_test_pattern(mt9m111, ctrl->val); 9108c2ecf20Sopenharmony_ci case V4L2_CID_COLORFX: 9118c2ecf20Sopenharmony_ci return mt9m111_set_colorfx(mt9m111, ctrl->val); 9128c2ecf20Sopenharmony_ci } 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci return -EINVAL; 9158c2ecf20Sopenharmony_ci} 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_cistatic int mt9m111_suspend(struct mt9m111 *mt9m111) 9188c2ecf20Sopenharmony_ci{ 9198c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); 9208c2ecf20Sopenharmony_ci int ret; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(mt9m111->gain, mt9m111_get_global_gain(mt9m111)); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci ret = reg_set(RESET, MT9M111_RESET_RESET_MODE); 9258c2ecf20Sopenharmony_ci if (!ret) 9268c2ecf20Sopenharmony_ci ret = reg_set(RESET, MT9M111_RESET_RESET_SOC | 9278c2ecf20Sopenharmony_ci MT9M111_RESET_OUTPUT_DISABLE | 9288c2ecf20Sopenharmony_ci MT9M111_RESET_ANALOG_STANDBY); 9298c2ecf20Sopenharmony_ci if (!ret) 9308c2ecf20Sopenharmony_ci ret = reg_clear(RESET, MT9M111_RESET_CHIP_ENABLE); 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci return ret; 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_cistatic void mt9m111_restore_state(struct mt9m111 *mt9m111) 9368c2ecf20Sopenharmony_ci{ 9378c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci mt9m111_set_context(mt9m111, mt9m111->ctx); 9408c2ecf20Sopenharmony_ci mt9m111_set_pixfmt(mt9m111, mt9m111->fmt->code); 9418c2ecf20Sopenharmony_ci mt9m111_setup_geometry(mt9m111, &mt9m111->rect, 9428c2ecf20Sopenharmony_ci mt9m111->width, mt9m111->height, mt9m111->fmt->code); 9438c2ecf20Sopenharmony_ci v4l2_ctrl_handler_setup(&mt9m111->hdl); 9448c2ecf20Sopenharmony_ci mt9m111_reg_mask(client, mt9m111->ctx->read_mode, 9458c2ecf20Sopenharmony_ci mt9m111->current_mode->reg_val, 9468c2ecf20Sopenharmony_ci mt9m111->current_mode->reg_mask); 9478c2ecf20Sopenharmony_ci} 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_cistatic int mt9m111_resume(struct mt9m111 *mt9m111) 9508c2ecf20Sopenharmony_ci{ 9518c2ecf20Sopenharmony_ci int ret = mt9m111_enable(mt9m111); 9528c2ecf20Sopenharmony_ci if (!ret) 9538c2ecf20Sopenharmony_ci ret = mt9m111_reset(mt9m111); 9548c2ecf20Sopenharmony_ci if (!ret) 9558c2ecf20Sopenharmony_ci mt9m111_restore_state(mt9m111); 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci return ret; 9588c2ecf20Sopenharmony_ci} 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_cistatic int mt9m111_init(struct mt9m111 *mt9m111) 9618c2ecf20Sopenharmony_ci{ 9628c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); 9638c2ecf20Sopenharmony_ci int ret; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci ret = mt9m111_enable(mt9m111); 9668c2ecf20Sopenharmony_ci if (!ret) 9678c2ecf20Sopenharmony_ci ret = mt9m111_reset(mt9m111); 9688c2ecf20Sopenharmony_ci if (!ret) 9698c2ecf20Sopenharmony_ci ret = mt9m111_set_context(mt9m111, mt9m111->ctx); 9708c2ecf20Sopenharmony_ci if (ret) 9718c2ecf20Sopenharmony_ci dev_err(&client->dev, "mt9m111 init failed: %d\n", ret); 9728c2ecf20Sopenharmony_ci return ret; 9738c2ecf20Sopenharmony_ci} 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_cistatic int mt9m111_power_on(struct mt9m111 *mt9m111) 9768c2ecf20Sopenharmony_ci{ 9778c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); 9788c2ecf20Sopenharmony_ci int ret; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci ret = v4l2_clk_enable(mt9m111->clk); 9818c2ecf20Sopenharmony_ci if (ret < 0) 9828c2ecf20Sopenharmony_ci return ret; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci ret = regulator_enable(mt9m111->regulator); 9858c2ecf20Sopenharmony_ci if (ret < 0) 9868c2ecf20Sopenharmony_ci goto out_clk_disable; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci ret = mt9m111_resume(mt9m111); 9898c2ecf20Sopenharmony_ci if (ret < 0) 9908c2ecf20Sopenharmony_ci goto out_regulator_disable; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci return 0; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ciout_regulator_disable: 9958c2ecf20Sopenharmony_ci regulator_disable(mt9m111->regulator); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ciout_clk_disable: 9988c2ecf20Sopenharmony_ci v4l2_clk_disable(mt9m111->clk); 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci dev_err(&client->dev, "Failed to resume the sensor: %d\n", ret); 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci return ret; 10038c2ecf20Sopenharmony_ci} 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_cistatic void mt9m111_power_off(struct mt9m111 *mt9m111) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci mt9m111_suspend(mt9m111); 10088c2ecf20Sopenharmony_ci regulator_disable(mt9m111->regulator); 10098c2ecf20Sopenharmony_ci v4l2_clk_disable(mt9m111->clk); 10108c2ecf20Sopenharmony_ci} 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_cistatic int mt9m111_s_power(struct v4l2_subdev *sd, int on) 10138c2ecf20Sopenharmony_ci{ 10148c2ecf20Sopenharmony_ci struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); 10158c2ecf20Sopenharmony_ci int ret = 0; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci mutex_lock(&mt9m111->power_lock); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci /* 10208c2ecf20Sopenharmony_ci * If the power count is modified from 0 to != 0 or from != 0 to 0, 10218c2ecf20Sopenharmony_ci * update the power state. 10228c2ecf20Sopenharmony_ci */ 10238c2ecf20Sopenharmony_ci if (mt9m111->power_count == !on) { 10248c2ecf20Sopenharmony_ci if (on) 10258c2ecf20Sopenharmony_ci ret = mt9m111_power_on(mt9m111); 10268c2ecf20Sopenharmony_ci else 10278c2ecf20Sopenharmony_ci mt9m111_power_off(mt9m111); 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci if (!ret) { 10318c2ecf20Sopenharmony_ci /* Update the power count. */ 10328c2ecf20Sopenharmony_ci mt9m111->power_count += on ? 1 : -1; 10338c2ecf20Sopenharmony_ci WARN_ON(mt9m111->power_count < 0); 10348c2ecf20Sopenharmony_ci } 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci mutex_unlock(&mt9m111->power_lock); 10378c2ecf20Sopenharmony_ci return ret; 10388c2ecf20Sopenharmony_ci} 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops mt9m111_ctrl_ops = { 10418c2ecf20Sopenharmony_ci .s_ctrl = mt9m111_s_ctrl, 10428c2ecf20Sopenharmony_ci}; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = { 10458c2ecf20Sopenharmony_ci .s_power = mt9m111_s_power, 10468c2ecf20Sopenharmony_ci .log_status = v4l2_ctrl_subdev_log_status, 10478c2ecf20Sopenharmony_ci .subscribe_event = v4l2_ctrl_subdev_subscribe_event, 10488c2ecf20Sopenharmony_ci .unsubscribe_event = v4l2_event_subdev_unsubscribe, 10498c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 10508c2ecf20Sopenharmony_ci .g_register = mt9m111_g_register, 10518c2ecf20Sopenharmony_ci .s_register = mt9m111_s_register, 10528c2ecf20Sopenharmony_ci#endif 10538c2ecf20Sopenharmony_ci}; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_cistatic int mt9m111_g_frame_interval(struct v4l2_subdev *sd, 10568c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_interval *fi) 10578c2ecf20Sopenharmony_ci{ 10588c2ecf20Sopenharmony_ci struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci fi->interval = mt9m111->frame_interval; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci return 0; 10638c2ecf20Sopenharmony_ci} 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_cistatic int mt9m111_s_frame_interval(struct v4l2_subdev *sd, 10668c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_interval *fi) 10678c2ecf20Sopenharmony_ci{ 10688c2ecf20Sopenharmony_ci struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); 10698c2ecf20Sopenharmony_ci const struct mt9m111_mode_info *mode; 10708c2ecf20Sopenharmony_ci struct v4l2_fract *fract = &fi->interval; 10718c2ecf20Sopenharmony_ci int fps; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci if (mt9m111->is_streaming) 10748c2ecf20Sopenharmony_ci return -EBUSY; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci if (fi->pad != 0) 10778c2ecf20Sopenharmony_ci return -EINVAL; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci if (fract->numerator == 0) { 10808c2ecf20Sopenharmony_ci fract->denominator = 30; 10818c2ecf20Sopenharmony_ci fract->numerator = 1; 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci fps = DIV_ROUND_CLOSEST(fract->denominator, fract->numerator); 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci /* Find best fitting mode. Do not update the mode if no one was found. */ 10878c2ecf20Sopenharmony_ci mode = mt9m111_find_mode(mt9m111, fps, mt9m111->width, mt9m111->height); 10888c2ecf20Sopenharmony_ci if (!mode) 10898c2ecf20Sopenharmony_ci return 0; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci if (mode->max_fps != fps) { 10928c2ecf20Sopenharmony_ci fract->denominator = mode->max_fps; 10938c2ecf20Sopenharmony_ci fract->numerator = 1; 10948c2ecf20Sopenharmony_ci } 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci mt9m111->current_mode = mode; 10978c2ecf20Sopenharmony_ci mt9m111->frame_interval = fi->interval; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci return 0; 11008c2ecf20Sopenharmony_ci} 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_cistatic int mt9m111_enum_mbus_code(struct v4l2_subdev *sd, 11038c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 11048c2ecf20Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 11058c2ecf20Sopenharmony_ci{ 11068c2ecf20Sopenharmony_ci if (code->pad || code->index >= ARRAY_SIZE(mt9m111_colour_fmts)) 11078c2ecf20Sopenharmony_ci return -EINVAL; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci code->code = mt9m111_colour_fmts[code->index].code; 11108c2ecf20Sopenharmony_ci return 0; 11118c2ecf20Sopenharmony_ci} 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_cistatic int mt9m111_s_stream(struct v4l2_subdev *sd, int enable) 11148c2ecf20Sopenharmony_ci{ 11158c2ecf20Sopenharmony_ci struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci mt9m111->is_streaming = !!enable; 11188c2ecf20Sopenharmony_ci return 0; 11198c2ecf20Sopenharmony_ci} 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_cistatic int mt9m111_init_cfg(struct v4l2_subdev *sd, 11228c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg) 11238c2ecf20Sopenharmony_ci{ 11248c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API 11258c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *format = 11268c2ecf20Sopenharmony_ci v4l2_subdev_get_try_format(sd, cfg, 0); 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci format->width = MT9M111_MAX_WIDTH; 11298c2ecf20Sopenharmony_ci format->height = MT9M111_MAX_HEIGHT; 11308c2ecf20Sopenharmony_ci format->code = mt9m111_colour_fmts[0].code; 11318c2ecf20Sopenharmony_ci format->colorspace = mt9m111_colour_fmts[0].colorspace; 11328c2ecf20Sopenharmony_ci format->field = V4L2_FIELD_NONE; 11338c2ecf20Sopenharmony_ci format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 11348c2ecf20Sopenharmony_ci format->quantization = V4L2_QUANTIZATION_DEFAULT; 11358c2ecf20Sopenharmony_ci format->xfer_func = V4L2_XFER_FUNC_DEFAULT; 11368c2ecf20Sopenharmony_ci#endif 11378c2ecf20Sopenharmony_ci return 0; 11388c2ecf20Sopenharmony_ci} 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_cistatic int mt9m111_get_mbus_config(struct v4l2_subdev *sd, 11418c2ecf20Sopenharmony_ci unsigned int pad, 11428c2ecf20Sopenharmony_ci struct v4l2_mbus_config *cfg) 11438c2ecf20Sopenharmony_ci{ 11448c2ecf20Sopenharmony_ci struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci cfg->flags = V4L2_MBUS_MASTER | 11478c2ecf20Sopenharmony_ci V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | 11488c2ecf20Sopenharmony_ci V4L2_MBUS_DATA_ACTIVE_HIGH; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci cfg->flags |= mt9m111->pclk_sample ? V4L2_MBUS_PCLK_SAMPLE_RISING : 11518c2ecf20Sopenharmony_ci V4L2_MBUS_PCLK_SAMPLE_FALLING; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci cfg->type = V4L2_MBUS_PARALLEL; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci return 0; 11568c2ecf20Sopenharmony_ci} 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = { 11598c2ecf20Sopenharmony_ci .s_stream = mt9m111_s_stream, 11608c2ecf20Sopenharmony_ci .g_frame_interval = mt9m111_g_frame_interval, 11618c2ecf20Sopenharmony_ci .s_frame_interval = mt9m111_s_frame_interval, 11628c2ecf20Sopenharmony_ci}; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops mt9m111_subdev_pad_ops = { 11658c2ecf20Sopenharmony_ci .init_cfg = mt9m111_init_cfg, 11668c2ecf20Sopenharmony_ci .enum_mbus_code = mt9m111_enum_mbus_code, 11678c2ecf20Sopenharmony_ci .get_selection = mt9m111_get_selection, 11688c2ecf20Sopenharmony_ci .set_selection = mt9m111_set_selection, 11698c2ecf20Sopenharmony_ci .get_fmt = mt9m111_get_fmt, 11708c2ecf20Sopenharmony_ci .set_fmt = mt9m111_set_fmt, 11718c2ecf20Sopenharmony_ci .get_mbus_config = mt9m111_get_mbus_config, 11728c2ecf20Sopenharmony_ci}; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops mt9m111_subdev_ops = { 11758c2ecf20Sopenharmony_ci .core = &mt9m111_subdev_core_ops, 11768c2ecf20Sopenharmony_ci .video = &mt9m111_subdev_video_ops, 11778c2ecf20Sopenharmony_ci .pad = &mt9m111_subdev_pad_ops, 11788c2ecf20Sopenharmony_ci}; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci/* 11818c2ecf20Sopenharmony_ci * Interface active, can use i2c. If it fails, it can indeed mean, that 11828c2ecf20Sopenharmony_ci * this wasn't our capture interface, so, we wait for the right one 11838c2ecf20Sopenharmony_ci */ 11848c2ecf20Sopenharmony_cistatic int mt9m111_video_probe(struct i2c_client *client) 11858c2ecf20Sopenharmony_ci{ 11868c2ecf20Sopenharmony_ci struct mt9m111 *mt9m111 = to_mt9m111(client); 11878c2ecf20Sopenharmony_ci s32 data; 11888c2ecf20Sopenharmony_ci int ret; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci ret = mt9m111_s_power(&mt9m111->subdev, 1); 11918c2ecf20Sopenharmony_ci if (ret < 0) 11928c2ecf20Sopenharmony_ci return ret; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci data = reg_read(CHIP_VERSION); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci switch (data) { 11978c2ecf20Sopenharmony_ci case 0x143a: /* MT9M111 or MT9M131 */ 11988c2ecf20Sopenharmony_ci dev_info(&client->dev, 11998c2ecf20Sopenharmony_ci "Detected a MT9M111/MT9M131 chip ID %x\n", data); 12008c2ecf20Sopenharmony_ci break; 12018c2ecf20Sopenharmony_ci case 0x148c: /* MT9M112 */ 12028c2ecf20Sopenharmony_ci dev_info(&client->dev, "Detected a MT9M112 chip ID %x\n", data); 12038c2ecf20Sopenharmony_ci break; 12048c2ecf20Sopenharmony_ci default: 12058c2ecf20Sopenharmony_ci dev_err(&client->dev, 12068c2ecf20Sopenharmony_ci "No MT9M111/MT9M112/MT9M131 chip detected register read %x\n", 12078c2ecf20Sopenharmony_ci data); 12088c2ecf20Sopenharmony_ci ret = -ENODEV; 12098c2ecf20Sopenharmony_ci goto done; 12108c2ecf20Sopenharmony_ci } 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci ret = mt9m111_init(mt9m111); 12138c2ecf20Sopenharmony_ci if (ret) 12148c2ecf20Sopenharmony_ci goto done; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci ret = v4l2_ctrl_handler_setup(&mt9m111->hdl); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_cidone: 12198c2ecf20Sopenharmony_ci mt9m111_s_power(&mt9m111->subdev, 0); 12208c2ecf20Sopenharmony_ci return ret; 12218c2ecf20Sopenharmony_ci} 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_cistatic int mt9m111_probe_fw(struct i2c_client *client, struct mt9m111 *mt9m111) 12248c2ecf20Sopenharmony_ci{ 12258c2ecf20Sopenharmony_ci struct v4l2_fwnode_endpoint bus_cfg = { 12268c2ecf20Sopenharmony_ci .bus_type = V4L2_MBUS_PARALLEL 12278c2ecf20Sopenharmony_ci }; 12288c2ecf20Sopenharmony_ci struct fwnode_handle *np; 12298c2ecf20Sopenharmony_ci int ret; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci np = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); 12328c2ecf20Sopenharmony_ci if (!np) 12338c2ecf20Sopenharmony_ci return -EINVAL; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci ret = v4l2_fwnode_endpoint_parse(np, &bus_cfg); 12368c2ecf20Sopenharmony_ci if (ret) 12378c2ecf20Sopenharmony_ci goto out_put_fw; 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci mt9m111->pclk_sample = !!(bus_cfg.bus.parallel.flags & 12408c2ecf20Sopenharmony_ci V4L2_MBUS_PCLK_SAMPLE_RISING); 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ciout_put_fw: 12438c2ecf20Sopenharmony_ci fwnode_handle_put(np); 12448c2ecf20Sopenharmony_ci return ret; 12458c2ecf20Sopenharmony_ci} 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_cistatic int mt9m111_probe(struct i2c_client *client) 12488c2ecf20Sopenharmony_ci{ 12498c2ecf20Sopenharmony_ci struct mt9m111 *mt9m111; 12508c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = client->adapter; 12518c2ecf20Sopenharmony_ci int ret; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { 12548c2ecf20Sopenharmony_ci dev_warn(&adapter->dev, 12558c2ecf20Sopenharmony_ci "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); 12568c2ecf20Sopenharmony_ci return -EIO; 12578c2ecf20Sopenharmony_ci } 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci mt9m111 = devm_kzalloc(&client->dev, sizeof(struct mt9m111), GFP_KERNEL); 12608c2ecf20Sopenharmony_ci if (!mt9m111) 12618c2ecf20Sopenharmony_ci return -ENOMEM; 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci if (dev_fwnode(&client->dev)) { 12648c2ecf20Sopenharmony_ci ret = mt9m111_probe_fw(client, mt9m111); 12658c2ecf20Sopenharmony_ci if (ret) 12668c2ecf20Sopenharmony_ci return ret; 12678c2ecf20Sopenharmony_ci } 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci mt9m111->clk = v4l2_clk_get(&client->dev, "mclk"); 12708c2ecf20Sopenharmony_ci if (IS_ERR(mt9m111->clk)) 12718c2ecf20Sopenharmony_ci return PTR_ERR(mt9m111->clk); 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci mt9m111->regulator = devm_regulator_get(&client->dev, "vdd"); 12748c2ecf20Sopenharmony_ci if (IS_ERR(mt9m111->regulator)) { 12758c2ecf20Sopenharmony_ci dev_err(&client->dev, "regulator not found: %ld\n", 12768c2ecf20Sopenharmony_ci PTR_ERR(mt9m111->regulator)); 12778c2ecf20Sopenharmony_ci return PTR_ERR(mt9m111->regulator); 12788c2ecf20Sopenharmony_ci } 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci /* Default HIGHPOWER context */ 12818c2ecf20Sopenharmony_ci mt9m111->ctx = &context_b; 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci v4l2_i2c_subdev_init(&mt9m111->subdev, client, &mt9m111_subdev_ops); 12848c2ecf20Sopenharmony_ci mt9m111->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | 12858c2ecf20Sopenharmony_ci V4L2_SUBDEV_FL_HAS_EVENTS; 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&mt9m111->hdl, 7); 12888c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops, 12898c2ecf20Sopenharmony_ci V4L2_CID_VFLIP, 0, 1, 1, 0); 12908c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops, 12918c2ecf20Sopenharmony_ci V4L2_CID_HFLIP, 0, 1, 1, 0); 12928c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops, 12938c2ecf20Sopenharmony_ci V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); 12948c2ecf20Sopenharmony_ci mt9m111->gain = v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops, 12958c2ecf20Sopenharmony_ci V4L2_CID_GAIN, 0, 63 * 2 * 2, 1, 32); 12968c2ecf20Sopenharmony_ci v4l2_ctrl_new_std_menu(&mt9m111->hdl, 12978c2ecf20Sopenharmony_ci &mt9m111_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, 12988c2ecf20Sopenharmony_ci V4L2_EXPOSURE_AUTO); 12998c2ecf20Sopenharmony_ci v4l2_ctrl_new_std_menu_items(&mt9m111->hdl, 13008c2ecf20Sopenharmony_ci &mt9m111_ctrl_ops, V4L2_CID_TEST_PATTERN, 13018c2ecf20Sopenharmony_ci ARRAY_SIZE(mt9m111_test_pattern_menu) - 1, 0, 0, 13028c2ecf20Sopenharmony_ci mt9m111_test_pattern_menu); 13038c2ecf20Sopenharmony_ci v4l2_ctrl_new_std_menu(&mt9m111->hdl, &mt9m111_ctrl_ops, 13048c2ecf20Sopenharmony_ci V4L2_CID_COLORFX, V4L2_COLORFX_SOLARIZATION, 13058c2ecf20Sopenharmony_ci ~(BIT(V4L2_COLORFX_NONE) | 13068c2ecf20Sopenharmony_ci BIT(V4L2_COLORFX_BW) | 13078c2ecf20Sopenharmony_ci BIT(V4L2_COLORFX_SEPIA) | 13088c2ecf20Sopenharmony_ci BIT(V4L2_COLORFX_NEGATIVE) | 13098c2ecf20Sopenharmony_ci BIT(V4L2_COLORFX_SOLARIZATION)), 13108c2ecf20Sopenharmony_ci V4L2_COLORFX_NONE); 13118c2ecf20Sopenharmony_ci mt9m111->subdev.ctrl_handler = &mt9m111->hdl; 13128c2ecf20Sopenharmony_ci if (mt9m111->hdl.error) { 13138c2ecf20Sopenharmony_ci ret = mt9m111->hdl.error; 13148c2ecf20Sopenharmony_ci goto out_clkput; 13158c2ecf20Sopenharmony_ci } 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER 13188c2ecf20Sopenharmony_ci mt9m111->pad.flags = MEDIA_PAD_FL_SOURCE; 13198c2ecf20Sopenharmony_ci mt9m111->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; 13208c2ecf20Sopenharmony_ci ret = media_entity_pads_init(&mt9m111->subdev.entity, 1, &mt9m111->pad); 13218c2ecf20Sopenharmony_ci if (ret < 0) 13228c2ecf20Sopenharmony_ci goto out_hdlfree; 13238c2ecf20Sopenharmony_ci#endif 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci mt9m111->current_mode = &mt9m111_mode_data[MT9M111_MODE_SXGA_15FPS]; 13268c2ecf20Sopenharmony_ci mt9m111->frame_interval.numerator = 1; 13278c2ecf20Sopenharmony_ci mt9m111->frame_interval.denominator = mt9m111->current_mode->max_fps; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci /* Second stage probe - when a capture adapter is there */ 13308c2ecf20Sopenharmony_ci mt9m111->rect.left = MT9M111_MIN_DARK_COLS; 13318c2ecf20Sopenharmony_ci mt9m111->rect.top = MT9M111_MIN_DARK_ROWS; 13328c2ecf20Sopenharmony_ci mt9m111->rect.width = MT9M111_MAX_WIDTH; 13338c2ecf20Sopenharmony_ci mt9m111->rect.height = MT9M111_MAX_HEIGHT; 13348c2ecf20Sopenharmony_ci mt9m111->width = mt9m111->rect.width; 13358c2ecf20Sopenharmony_ci mt9m111->height = mt9m111->rect.height; 13368c2ecf20Sopenharmony_ci mt9m111->fmt = &mt9m111_colour_fmts[0]; 13378c2ecf20Sopenharmony_ci mt9m111->lastpage = -1; 13388c2ecf20Sopenharmony_ci mutex_init(&mt9m111->power_lock); 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci ret = mt9m111_video_probe(client); 13418c2ecf20Sopenharmony_ci if (ret < 0) 13428c2ecf20Sopenharmony_ci goto out_entityclean; 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci mt9m111->subdev.dev = &client->dev; 13458c2ecf20Sopenharmony_ci ret = v4l2_async_register_subdev(&mt9m111->subdev); 13468c2ecf20Sopenharmony_ci if (ret < 0) 13478c2ecf20Sopenharmony_ci goto out_entityclean; 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci return 0; 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ciout_entityclean: 13528c2ecf20Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER 13538c2ecf20Sopenharmony_ci media_entity_cleanup(&mt9m111->subdev.entity); 13548c2ecf20Sopenharmony_ciout_hdlfree: 13558c2ecf20Sopenharmony_ci#endif 13568c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&mt9m111->hdl); 13578c2ecf20Sopenharmony_ciout_clkput: 13588c2ecf20Sopenharmony_ci v4l2_clk_put(mt9m111->clk); 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci return ret; 13618c2ecf20Sopenharmony_ci} 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_cistatic int mt9m111_remove(struct i2c_client *client) 13648c2ecf20Sopenharmony_ci{ 13658c2ecf20Sopenharmony_ci struct mt9m111 *mt9m111 = to_mt9m111(client); 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci v4l2_async_unregister_subdev(&mt9m111->subdev); 13688c2ecf20Sopenharmony_ci media_entity_cleanup(&mt9m111->subdev.entity); 13698c2ecf20Sopenharmony_ci v4l2_clk_put(mt9m111->clk); 13708c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&mt9m111->hdl); 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci return 0; 13738c2ecf20Sopenharmony_ci} 13748c2ecf20Sopenharmony_cistatic const struct of_device_id mt9m111_of_match[] = { 13758c2ecf20Sopenharmony_ci { .compatible = "micron,mt9m111", }, 13768c2ecf20Sopenharmony_ci {}, 13778c2ecf20Sopenharmony_ci}; 13788c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mt9m111_of_match); 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_cistatic const struct i2c_device_id mt9m111_id[] = { 13818c2ecf20Sopenharmony_ci { "mt9m111", 0 }, 13828c2ecf20Sopenharmony_ci { } 13838c2ecf20Sopenharmony_ci}; 13848c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, mt9m111_id); 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_cistatic struct i2c_driver mt9m111_i2c_driver = { 13878c2ecf20Sopenharmony_ci .driver = { 13888c2ecf20Sopenharmony_ci .name = "mt9m111", 13898c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(mt9m111_of_match), 13908c2ecf20Sopenharmony_ci }, 13918c2ecf20Sopenharmony_ci .probe_new = mt9m111_probe, 13928c2ecf20Sopenharmony_ci .remove = mt9m111_remove, 13938c2ecf20Sopenharmony_ci .id_table = mt9m111_id, 13948c2ecf20Sopenharmony_ci}; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_cimodule_i2c_driver(mt9m111_i2c_driver); 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Micron/Aptina MT9M111/MT9M112/MT9M131 Camera driver"); 13998c2ecf20Sopenharmony_ciMODULE_AUTHOR("Robert Jarzmik"); 14008c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1401