162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Samsung S5P/EXYNOS SoC series MIPI-CSI receiver driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd. 662306a36Sopenharmony_ci * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/clk.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/device.h> 1262306a36Sopenharmony_ci#include <linux/errno.h> 1362306a36Sopenharmony_ci#include <linux/interrupt.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/irq.h> 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/memory.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/of.h> 2062306a36Sopenharmony_ci#include <linux/of_graph.h> 2162306a36Sopenharmony_ci#include <linux/phy/phy.h> 2262306a36Sopenharmony_ci#include <linux/platform_device.h> 2362306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2462306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 2562306a36Sopenharmony_ci#include <linux/sizes.h> 2662306a36Sopenharmony_ci#include <linux/slab.h> 2762306a36Sopenharmony_ci#include <linux/spinlock.h> 2862306a36Sopenharmony_ci#include <linux/videodev2.h> 2962306a36Sopenharmony_ci#include <media/drv-intf/exynos-fimc.h> 3062306a36Sopenharmony_ci#include <media/v4l2-fwnode.h> 3162306a36Sopenharmony_ci#include <media/v4l2-subdev.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include "mipi-csis.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int debug; 3662306a36Sopenharmony_cimodule_param(debug, int, 0644); 3762306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "Debug level (0-2)"); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* Register map definition */ 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* CSIS global control */ 4262306a36Sopenharmony_ci#define S5PCSIS_CTRL 0x00 4362306a36Sopenharmony_ci#define S5PCSIS_CTRL_DPDN_DEFAULT (0 << 31) 4462306a36Sopenharmony_ci#define S5PCSIS_CTRL_DPDN_SWAP (1UL << 31) 4562306a36Sopenharmony_ci#define S5PCSIS_CTRL_ALIGN_32BIT (1 << 20) 4662306a36Sopenharmony_ci#define S5PCSIS_CTRL_UPDATE_SHADOW (1 << 16) 4762306a36Sopenharmony_ci#define S5PCSIS_CTRL_WCLK_EXTCLK (1 << 8) 4862306a36Sopenharmony_ci#define S5PCSIS_CTRL_RESET (1 << 4) 4962306a36Sopenharmony_ci#define S5PCSIS_CTRL_ENABLE (1 << 0) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* D-PHY control */ 5262306a36Sopenharmony_ci#define S5PCSIS_DPHYCTRL 0x04 5362306a36Sopenharmony_ci#define S5PCSIS_DPHYCTRL_HSS_MASK (0x1f << 27) 5462306a36Sopenharmony_ci#define S5PCSIS_DPHYCTRL_ENABLE (0x1f << 0) 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define S5PCSIS_CONFIG 0x08 5762306a36Sopenharmony_ci#define S5PCSIS_CFG_FMT_YCBCR422_8BIT (0x1e << 2) 5862306a36Sopenharmony_ci#define S5PCSIS_CFG_FMT_RAW8 (0x2a << 2) 5962306a36Sopenharmony_ci#define S5PCSIS_CFG_FMT_RAW10 (0x2b << 2) 6062306a36Sopenharmony_ci#define S5PCSIS_CFG_FMT_RAW12 (0x2c << 2) 6162306a36Sopenharmony_ci/* User defined formats, x = 1...4 */ 6262306a36Sopenharmony_ci#define S5PCSIS_CFG_FMT_USER(x) ((0x30 + x - 1) << 2) 6362306a36Sopenharmony_ci#define S5PCSIS_CFG_FMT_MASK (0x3f << 2) 6462306a36Sopenharmony_ci#define S5PCSIS_CFG_NR_LANE_MASK 3 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* Interrupt mask */ 6762306a36Sopenharmony_ci#define S5PCSIS_INTMSK 0x10 6862306a36Sopenharmony_ci#define S5PCSIS_INTMSK_EVEN_BEFORE (1UL << 31) 6962306a36Sopenharmony_ci#define S5PCSIS_INTMSK_EVEN_AFTER (1 << 30) 7062306a36Sopenharmony_ci#define S5PCSIS_INTMSK_ODD_BEFORE (1 << 29) 7162306a36Sopenharmony_ci#define S5PCSIS_INTMSK_ODD_AFTER (1 << 28) 7262306a36Sopenharmony_ci#define S5PCSIS_INTMSK_FRAME_START (1 << 27) 7362306a36Sopenharmony_ci#define S5PCSIS_INTMSK_FRAME_END (1 << 26) 7462306a36Sopenharmony_ci#define S5PCSIS_INTMSK_ERR_SOT_HS (1 << 12) 7562306a36Sopenharmony_ci#define S5PCSIS_INTMSK_ERR_LOST_FS (1 << 5) 7662306a36Sopenharmony_ci#define S5PCSIS_INTMSK_ERR_LOST_FE (1 << 4) 7762306a36Sopenharmony_ci#define S5PCSIS_INTMSK_ERR_OVER (1 << 3) 7862306a36Sopenharmony_ci#define S5PCSIS_INTMSK_ERR_ECC (1 << 2) 7962306a36Sopenharmony_ci#define S5PCSIS_INTMSK_ERR_CRC (1 << 1) 8062306a36Sopenharmony_ci#define S5PCSIS_INTMSK_ERR_UNKNOWN (1 << 0) 8162306a36Sopenharmony_ci#define S5PCSIS_INTMSK_EXYNOS4_EN_ALL 0xf000103f 8262306a36Sopenharmony_ci#define S5PCSIS_INTMSK_EXYNOS5_EN_ALL 0xfc00103f 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* Interrupt source */ 8562306a36Sopenharmony_ci#define S5PCSIS_INTSRC 0x14 8662306a36Sopenharmony_ci#define S5PCSIS_INTSRC_EVEN_BEFORE (1UL << 31) 8762306a36Sopenharmony_ci#define S5PCSIS_INTSRC_EVEN_AFTER (1 << 30) 8862306a36Sopenharmony_ci#define S5PCSIS_INTSRC_EVEN (0x3 << 30) 8962306a36Sopenharmony_ci#define S5PCSIS_INTSRC_ODD_BEFORE (1 << 29) 9062306a36Sopenharmony_ci#define S5PCSIS_INTSRC_ODD_AFTER (1 << 28) 9162306a36Sopenharmony_ci#define S5PCSIS_INTSRC_ODD (0x3 << 28) 9262306a36Sopenharmony_ci#define S5PCSIS_INTSRC_NON_IMAGE_DATA (0xf << 28) 9362306a36Sopenharmony_ci#define S5PCSIS_INTSRC_FRAME_START (1 << 27) 9462306a36Sopenharmony_ci#define S5PCSIS_INTSRC_FRAME_END (1 << 26) 9562306a36Sopenharmony_ci#define S5PCSIS_INTSRC_ERR_SOT_HS (0xf << 12) 9662306a36Sopenharmony_ci#define S5PCSIS_INTSRC_ERR_LOST_FS (1 << 5) 9762306a36Sopenharmony_ci#define S5PCSIS_INTSRC_ERR_LOST_FE (1 << 4) 9862306a36Sopenharmony_ci#define S5PCSIS_INTSRC_ERR_OVER (1 << 3) 9962306a36Sopenharmony_ci#define S5PCSIS_INTSRC_ERR_ECC (1 << 2) 10062306a36Sopenharmony_ci#define S5PCSIS_INTSRC_ERR_CRC (1 << 1) 10162306a36Sopenharmony_ci#define S5PCSIS_INTSRC_ERR_UNKNOWN (1 << 0) 10262306a36Sopenharmony_ci#define S5PCSIS_INTSRC_ERRORS 0xf03f 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* Pixel resolution */ 10562306a36Sopenharmony_ci#define S5PCSIS_RESOL 0x2c 10662306a36Sopenharmony_ci#define CSIS_MAX_PIX_WIDTH 0xffff 10762306a36Sopenharmony_ci#define CSIS_MAX_PIX_HEIGHT 0xffff 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/* Non-image packet data buffers */ 11062306a36Sopenharmony_ci#define S5PCSIS_PKTDATA_ODD 0x2000 11162306a36Sopenharmony_ci#define S5PCSIS_PKTDATA_EVEN 0x3000 11262306a36Sopenharmony_ci#define S5PCSIS_PKTDATA_SIZE SZ_4K 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cienum { 11562306a36Sopenharmony_ci CSIS_CLK_MUX, 11662306a36Sopenharmony_ci CSIS_CLK_GATE, 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic char *csi_clock_name[] = { 12062306a36Sopenharmony_ci [CSIS_CLK_MUX] = "sclk_csis", 12162306a36Sopenharmony_ci [CSIS_CLK_GATE] = "csis", 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci#define NUM_CSIS_CLOCKS ARRAY_SIZE(csi_clock_name) 12462306a36Sopenharmony_ci#define DEFAULT_SCLK_CSIS_FREQ 166000000UL 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic const char * const csis_supply_name[] = { 12762306a36Sopenharmony_ci "vddcore", /* CSIS Core (1.0V, 1.1V or 1.2V) supply */ 12862306a36Sopenharmony_ci "vddio", /* CSIS I/O and PLL (1.8V) supply */ 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci#define CSIS_NUM_SUPPLIES ARRAY_SIZE(csis_supply_name) 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cienum { 13362306a36Sopenharmony_ci ST_POWERED = 1, 13462306a36Sopenharmony_ci ST_STREAMING = 2, 13562306a36Sopenharmony_ci ST_SUSPENDED = 4, 13662306a36Sopenharmony_ci}; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistruct s5pcsis_event { 13962306a36Sopenharmony_ci u32 mask; 14062306a36Sopenharmony_ci const char * const name; 14162306a36Sopenharmony_ci unsigned int counter; 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic const struct s5pcsis_event s5pcsis_events[] = { 14562306a36Sopenharmony_ci /* Errors */ 14662306a36Sopenharmony_ci { S5PCSIS_INTSRC_ERR_SOT_HS, "SOT Error" }, 14762306a36Sopenharmony_ci { S5PCSIS_INTSRC_ERR_LOST_FS, "Lost Frame Start Error" }, 14862306a36Sopenharmony_ci { S5PCSIS_INTSRC_ERR_LOST_FE, "Lost Frame End Error" }, 14962306a36Sopenharmony_ci { S5PCSIS_INTSRC_ERR_OVER, "FIFO Overflow Error" }, 15062306a36Sopenharmony_ci { S5PCSIS_INTSRC_ERR_ECC, "ECC Error" }, 15162306a36Sopenharmony_ci { S5PCSIS_INTSRC_ERR_CRC, "CRC Error" }, 15262306a36Sopenharmony_ci { S5PCSIS_INTSRC_ERR_UNKNOWN, "Unknown Error" }, 15362306a36Sopenharmony_ci /* Non-image data receive events */ 15462306a36Sopenharmony_ci { S5PCSIS_INTSRC_EVEN_BEFORE, "Non-image data before even frame" }, 15562306a36Sopenharmony_ci { S5PCSIS_INTSRC_EVEN_AFTER, "Non-image data after even frame" }, 15662306a36Sopenharmony_ci { S5PCSIS_INTSRC_ODD_BEFORE, "Non-image data before odd frame" }, 15762306a36Sopenharmony_ci { S5PCSIS_INTSRC_ODD_AFTER, "Non-image data after odd frame" }, 15862306a36Sopenharmony_ci /* Frame start/end */ 15962306a36Sopenharmony_ci { S5PCSIS_INTSRC_FRAME_START, "Frame Start" }, 16062306a36Sopenharmony_ci { S5PCSIS_INTSRC_FRAME_END, "Frame End" }, 16162306a36Sopenharmony_ci}; 16262306a36Sopenharmony_ci#define S5PCSIS_NUM_EVENTS ARRAY_SIZE(s5pcsis_events) 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistruct csis_pktbuf { 16562306a36Sopenharmony_ci u32 *data; 16662306a36Sopenharmony_ci unsigned int len; 16762306a36Sopenharmony_ci}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistruct csis_drvdata { 17062306a36Sopenharmony_ci /* Mask of all used interrupts in S5PCSIS_INTMSK register */ 17162306a36Sopenharmony_ci u32 interrupt_mask; 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/** 17562306a36Sopenharmony_ci * struct csis_state - the driver's internal state data structure 17662306a36Sopenharmony_ci * @lock: mutex serializing the subdev and power management operations, 17762306a36Sopenharmony_ci * protecting @format and @flags members 17862306a36Sopenharmony_ci * @pads: CSIS pads array 17962306a36Sopenharmony_ci * @sd: v4l2_subdev associated with CSIS device instance 18062306a36Sopenharmony_ci * @index: the hardware instance index 18162306a36Sopenharmony_ci * @pdev: CSIS platform device 18262306a36Sopenharmony_ci * @phy: pointer to the CSIS generic PHY 18362306a36Sopenharmony_ci * @regs: mmapped I/O registers memory 18462306a36Sopenharmony_ci * @supplies: CSIS regulator supplies 18562306a36Sopenharmony_ci * @clock: CSIS clocks 18662306a36Sopenharmony_ci * @irq: requested s5p-mipi-csis irq number 18762306a36Sopenharmony_ci * @interrupt_mask: interrupt mask of the all used interrupts 18862306a36Sopenharmony_ci * @flags: the state variable for power and streaming control 18962306a36Sopenharmony_ci * @clk_frequency: device bus clock frequency 19062306a36Sopenharmony_ci * @hs_settle: HS-RX settle time 19162306a36Sopenharmony_ci * @num_lanes: number of MIPI-CSI data lanes used 19262306a36Sopenharmony_ci * @max_num_lanes: maximum number of MIPI-CSI data lanes supported 19362306a36Sopenharmony_ci * @wclk_ext: CSI wrapper clock: 0 - bus clock, 1 - external SCLK_CAM 19462306a36Sopenharmony_ci * @csis_fmt: current CSIS pixel format 19562306a36Sopenharmony_ci * @format: common media bus format for the source and sink pad 19662306a36Sopenharmony_ci * @slock: spinlock protecting structure members below 19762306a36Sopenharmony_ci * @pkt_buf: the frame embedded (non-image) data buffer 19862306a36Sopenharmony_ci * @events: MIPI-CSIS event (error) counters 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_cistruct csis_state { 20162306a36Sopenharmony_ci struct mutex lock; 20262306a36Sopenharmony_ci struct media_pad pads[CSIS_PADS_NUM]; 20362306a36Sopenharmony_ci struct v4l2_subdev sd; 20462306a36Sopenharmony_ci u8 index; 20562306a36Sopenharmony_ci struct platform_device *pdev; 20662306a36Sopenharmony_ci struct phy *phy; 20762306a36Sopenharmony_ci void __iomem *regs; 20862306a36Sopenharmony_ci struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES]; 20962306a36Sopenharmony_ci struct clk *clock[NUM_CSIS_CLOCKS]; 21062306a36Sopenharmony_ci int irq; 21162306a36Sopenharmony_ci u32 interrupt_mask; 21262306a36Sopenharmony_ci u32 flags; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci u32 clk_frequency; 21562306a36Sopenharmony_ci u32 hs_settle; 21662306a36Sopenharmony_ci u32 num_lanes; 21762306a36Sopenharmony_ci u32 max_num_lanes; 21862306a36Sopenharmony_ci u8 wclk_ext; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci const struct csis_pix_format *csis_fmt; 22162306a36Sopenharmony_ci struct v4l2_mbus_framefmt format; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci spinlock_t slock; 22462306a36Sopenharmony_ci struct csis_pktbuf pkt_buf; 22562306a36Sopenharmony_ci struct s5pcsis_event events[S5PCSIS_NUM_EVENTS]; 22662306a36Sopenharmony_ci}; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci/** 22962306a36Sopenharmony_ci * struct csis_pix_format - CSIS pixel format description 23062306a36Sopenharmony_ci * @pix_width_alignment: horizontal pixel alignment, width will be 23162306a36Sopenharmony_ci * multiple of 2^pix_width_alignment 23262306a36Sopenharmony_ci * @code: corresponding media bus code 23362306a36Sopenharmony_ci * @fmt_reg: S5PCSIS_CONFIG register value 23462306a36Sopenharmony_ci * @data_alignment: MIPI-CSI data alignment in bits 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_cistruct csis_pix_format { 23762306a36Sopenharmony_ci unsigned int pix_width_alignment; 23862306a36Sopenharmony_ci u32 code; 23962306a36Sopenharmony_ci u32 fmt_reg; 24062306a36Sopenharmony_ci u8 data_alignment; 24162306a36Sopenharmony_ci}; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic const struct csis_pix_format s5pcsis_formats[] = { 24462306a36Sopenharmony_ci { 24562306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_VYUY8_2X8, 24662306a36Sopenharmony_ci .fmt_reg = S5PCSIS_CFG_FMT_YCBCR422_8BIT, 24762306a36Sopenharmony_ci .data_alignment = 32, 24862306a36Sopenharmony_ci }, { 24962306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_JPEG_1X8, 25062306a36Sopenharmony_ci .fmt_reg = S5PCSIS_CFG_FMT_USER(1), 25162306a36Sopenharmony_ci .data_alignment = 32, 25262306a36Sopenharmony_ci }, { 25362306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_S5C_UYVY_JPEG_1X8, 25462306a36Sopenharmony_ci .fmt_reg = S5PCSIS_CFG_FMT_USER(1), 25562306a36Sopenharmony_ci .data_alignment = 32, 25662306a36Sopenharmony_ci }, { 25762306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_SGRBG8_1X8, 25862306a36Sopenharmony_ci .fmt_reg = S5PCSIS_CFG_FMT_RAW8, 25962306a36Sopenharmony_ci .data_alignment = 24, 26062306a36Sopenharmony_ci }, { 26162306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_SGRBG10_1X10, 26262306a36Sopenharmony_ci .fmt_reg = S5PCSIS_CFG_FMT_RAW10, 26362306a36Sopenharmony_ci .data_alignment = 24, 26462306a36Sopenharmony_ci }, { 26562306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_SGRBG12_1X12, 26662306a36Sopenharmony_ci .fmt_reg = S5PCSIS_CFG_FMT_RAW12, 26762306a36Sopenharmony_ci .data_alignment = 24, 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci}; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci#define s5pcsis_write(__csis, __r, __v) writel(__v, __csis->regs + __r) 27262306a36Sopenharmony_ci#define s5pcsis_read(__csis, __r) readl(__csis->regs + __r) 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic struct csis_state *sd_to_csis_state(struct v4l2_subdev *sdev) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci return container_of(sdev, struct csis_state, sd); 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic const struct csis_pix_format *find_csis_format( 28062306a36Sopenharmony_ci struct v4l2_mbus_framefmt *mf) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci int i; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(s5pcsis_formats); i++) 28562306a36Sopenharmony_ci if (mf->code == s5pcsis_formats[i].code) 28662306a36Sopenharmony_ci return &s5pcsis_formats[i]; 28762306a36Sopenharmony_ci return NULL; 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic void s5pcsis_enable_interrupts(struct csis_state *state, bool on) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci u32 val = s5pcsis_read(state, S5PCSIS_INTMSK); 29362306a36Sopenharmony_ci if (on) 29462306a36Sopenharmony_ci val |= state->interrupt_mask; 29562306a36Sopenharmony_ci else 29662306a36Sopenharmony_ci val &= ~state->interrupt_mask; 29762306a36Sopenharmony_ci s5pcsis_write(state, S5PCSIS_INTMSK, val); 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic void s5pcsis_reset(struct csis_state *state) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci u32 val = s5pcsis_read(state, S5PCSIS_CTRL); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci s5pcsis_write(state, S5PCSIS_CTRL, val | S5PCSIS_CTRL_RESET); 30562306a36Sopenharmony_ci udelay(10); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic void s5pcsis_system_enable(struct csis_state *state, int on) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci u32 val, mask; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci val = s5pcsis_read(state, S5PCSIS_CTRL); 31362306a36Sopenharmony_ci if (on) 31462306a36Sopenharmony_ci val |= S5PCSIS_CTRL_ENABLE; 31562306a36Sopenharmony_ci else 31662306a36Sopenharmony_ci val &= ~S5PCSIS_CTRL_ENABLE; 31762306a36Sopenharmony_ci s5pcsis_write(state, S5PCSIS_CTRL, val); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci val = s5pcsis_read(state, S5PCSIS_DPHYCTRL); 32062306a36Sopenharmony_ci val &= ~S5PCSIS_DPHYCTRL_ENABLE; 32162306a36Sopenharmony_ci if (on) { 32262306a36Sopenharmony_ci mask = (1 << (state->num_lanes + 1)) - 1; 32362306a36Sopenharmony_ci val |= (mask & S5PCSIS_DPHYCTRL_ENABLE); 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci s5pcsis_write(state, S5PCSIS_DPHYCTRL, val); 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci/* Called with the state.lock mutex held */ 32962306a36Sopenharmony_cistatic void __s5pcsis_set_format(struct csis_state *state) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &state->format; 33262306a36Sopenharmony_ci u32 val; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci v4l2_dbg(1, debug, &state->sd, "fmt: %#x, %d x %d\n", 33562306a36Sopenharmony_ci mf->code, mf->width, mf->height); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* Color format */ 33862306a36Sopenharmony_ci val = s5pcsis_read(state, S5PCSIS_CONFIG); 33962306a36Sopenharmony_ci val = (val & ~S5PCSIS_CFG_FMT_MASK) | state->csis_fmt->fmt_reg; 34062306a36Sopenharmony_ci s5pcsis_write(state, S5PCSIS_CONFIG, val); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* Pixel resolution */ 34362306a36Sopenharmony_ci val = (mf->width << 16) | mf->height; 34462306a36Sopenharmony_ci s5pcsis_write(state, S5PCSIS_RESOL, val); 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic void s5pcsis_set_hsync_settle(struct csis_state *state, int settle) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci u32 val = s5pcsis_read(state, S5PCSIS_DPHYCTRL); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci val = (val & ~S5PCSIS_DPHYCTRL_HSS_MASK) | (settle << 27); 35262306a36Sopenharmony_ci s5pcsis_write(state, S5PCSIS_DPHYCTRL, val); 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic void s5pcsis_set_params(struct csis_state *state) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci u32 val; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci val = s5pcsis_read(state, S5PCSIS_CONFIG); 36062306a36Sopenharmony_ci val = (val & ~S5PCSIS_CFG_NR_LANE_MASK) | (state->num_lanes - 1); 36162306a36Sopenharmony_ci s5pcsis_write(state, S5PCSIS_CONFIG, val); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci __s5pcsis_set_format(state); 36462306a36Sopenharmony_ci s5pcsis_set_hsync_settle(state, state->hs_settle); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci val = s5pcsis_read(state, S5PCSIS_CTRL); 36762306a36Sopenharmony_ci if (state->csis_fmt->data_alignment == 32) 36862306a36Sopenharmony_ci val |= S5PCSIS_CTRL_ALIGN_32BIT; 36962306a36Sopenharmony_ci else /* 24-bits */ 37062306a36Sopenharmony_ci val &= ~S5PCSIS_CTRL_ALIGN_32BIT; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci val &= ~S5PCSIS_CTRL_WCLK_EXTCLK; 37362306a36Sopenharmony_ci if (state->wclk_ext) 37462306a36Sopenharmony_ci val |= S5PCSIS_CTRL_WCLK_EXTCLK; 37562306a36Sopenharmony_ci s5pcsis_write(state, S5PCSIS_CTRL, val); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* Update the shadow register. */ 37862306a36Sopenharmony_ci val = s5pcsis_read(state, S5PCSIS_CTRL); 37962306a36Sopenharmony_ci s5pcsis_write(state, S5PCSIS_CTRL, val | S5PCSIS_CTRL_UPDATE_SHADOW); 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic void s5pcsis_clk_put(struct csis_state *state) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci int i; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci for (i = 0; i < NUM_CSIS_CLOCKS; i++) { 38762306a36Sopenharmony_ci if (IS_ERR(state->clock[i])) 38862306a36Sopenharmony_ci continue; 38962306a36Sopenharmony_ci clk_unprepare(state->clock[i]); 39062306a36Sopenharmony_ci clk_put(state->clock[i]); 39162306a36Sopenharmony_ci state->clock[i] = ERR_PTR(-EINVAL); 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic int s5pcsis_clk_get(struct csis_state *state) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci struct device *dev = &state->pdev->dev; 39862306a36Sopenharmony_ci int i, ret; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci for (i = 0; i < NUM_CSIS_CLOCKS; i++) 40162306a36Sopenharmony_ci state->clock[i] = ERR_PTR(-EINVAL); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci for (i = 0; i < NUM_CSIS_CLOCKS; i++) { 40462306a36Sopenharmony_ci state->clock[i] = clk_get(dev, csi_clock_name[i]); 40562306a36Sopenharmony_ci if (IS_ERR(state->clock[i])) { 40662306a36Sopenharmony_ci ret = PTR_ERR(state->clock[i]); 40762306a36Sopenharmony_ci goto err; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci ret = clk_prepare(state->clock[i]); 41062306a36Sopenharmony_ci if (ret < 0) { 41162306a36Sopenharmony_ci clk_put(state->clock[i]); 41262306a36Sopenharmony_ci state->clock[i] = ERR_PTR(-EINVAL); 41362306a36Sopenharmony_ci goto err; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci return 0; 41762306a36Sopenharmony_cierr: 41862306a36Sopenharmony_ci s5pcsis_clk_put(state); 41962306a36Sopenharmony_ci dev_err(dev, "failed to get clock: %s\n", csi_clock_name[i]); 42062306a36Sopenharmony_ci return ret; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic void dump_regs(struct csis_state *state, const char *label) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci struct { 42662306a36Sopenharmony_ci u32 offset; 42762306a36Sopenharmony_ci const char * const name; 42862306a36Sopenharmony_ci } registers[] = { 42962306a36Sopenharmony_ci { 0x00, "CTRL" }, 43062306a36Sopenharmony_ci { 0x04, "DPHYCTRL" }, 43162306a36Sopenharmony_ci { 0x08, "CONFIG" }, 43262306a36Sopenharmony_ci { 0x0c, "DPHYSTS" }, 43362306a36Sopenharmony_ci { 0x10, "INTMSK" }, 43462306a36Sopenharmony_ci { 0x2c, "RESOL" }, 43562306a36Sopenharmony_ci { 0x38, "SDW_CONFIG" }, 43662306a36Sopenharmony_ci }; 43762306a36Sopenharmony_ci u32 i; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci v4l2_info(&state->sd, "--- %s ---\n", label); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(registers); i++) { 44262306a36Sopenharmony_ci u32 cfg = s5pcsis_read(state, registers[i].offset); 44362306a36Sopenharmony_ci v4l2_info(&state->sd, "%10s: 0x%08x\n", registers[i].name, cfg); 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic void s5pcsis_start_stream(struct csis_state *state) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci s5pcsis_reset(state); 45062306a36Sopenharmony_ci s5pcsis_set_params(state); 45162306a36Sopenharmony_ci s5pcsis_system_enable(state, true); 45262306a36Sopenharmony_ci s5pcsis_enable_interrupts(state, true); 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic void s5pcsis_stop_stream(struct csis_state *state) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci s5pcsis_enable_interrupts(state, false); 45862306a36Sopenharmony_ci s5pcsis_system_enable(state, false); 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic void s5pcsis_clear_counters(struct csis_state *state) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci unsigned long flags; 46462306a36Sopenharmony_ci int i; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci spin_lock_irqsave(&state->slock, flags); 46762306a36Sopenharmony_ci for (i = 0; i < S5PCSIS_NUM_EVENTS; i++) 46862306a36Sopenharmony_ci state->events[i].counter = 0; 46962306a36Sopenharmony_ci spin_unlock_irqrestore(&state->slock, flags); 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic void s5pcsis_log_counters(struct csis_state *state, bool non_errors) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci int i = non_errors ? S5PCSIS_NUM_EVENTS : S5PCSIS_NUM_EVENTS - 4; 47562306a36Sopenharmony_ci unsigned long flags; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci spin_lock_irqsave(&state->slock, flags); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci for (i--; i >= 0; i--) { 48062306a36Sopenharmony_ci if (state->events[i].counter > 0 || debug) 48162306a36Sopenharmony_ci v4l2_info(&state->sd, "%s events: %d\n", 48262306a36Sopenharmony_ci state->events[i].name, 48362306a36Sopenharmony_ci state->events[i].counter); 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci spin_unlock_irqrestore(&state->slock, flags); 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci/* 48962306a36Sopenharmony_ci * V4L2 subdev operations 49062306a36Sopenharmony_ci */ 49162306a36Sopenharmony_cistatic int s5pcsis_s_power(struct v4l2_subdev *sd, int on) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci struct csis_state *state = sd_to_csis_state(sd); 49462306a36Sopenharmony_ci struct device *dev = &state->pdev->dev; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if (on) 49762306a36Sopenharmony_ci return pm_runtime_resume_and_get(dev); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci return pm_runtime_put_sync(dev); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic int s5pcsis_s_stream(struct v4l2_subdev *sd, int enable) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci struct csis_state *state = sd_to_csis_state(sd); 50562306a36Sopenharmony_ci int ret = 0; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: %d, state: 0x%x\n", 50862306a36Sopenharmony_ci __func__, enable, state->flags); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (enable) { 51162306a36Sopenharmony_ci s5pcsis_clear_counters(state); 51262306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(&state->pdev->dev); 51362306a36Sopenharmony_ci if (ret < 0) 51462306a36Sopenharmony_ci return ret; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci mutex_lock(&state->lock); 51862306a36Sopenharmony_ci if (enable) { 51962306a36Sopenharmony_ci if (state->flags & ST_SUSPENDED) { 52062306a36Sopenharmony_ci ret = -EBUSY; 52162306a36Sopenharmony_ci goto unlock; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci s5pcsis_start_stream(state); 52462306a36Sopenharmony_ci state->flags |= ST_STREAMING; 52562306a36Sopenharmony_ci } else { 52662306a36Sopenharmony_ci s5pcsis_stop_stream(state); 52762306a36Sopenharmony_ci state->flags &= ~ST_STREAMING; 52862306a36Sopenharmony_ci if (debug > 0) 52962306a36Sopenharmony_ci s5pcsis_log_counters(state, true); 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ciunlock: 53262306a36Sopenharmony_ci mutex_unlock(&state->lock); 53362306a36Sopenharmony_ci if (!enable) 53462306a36Sopenharmony_ci pm_runtime_put(&state->pdev->dev); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci return ret; 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic int s5pcsis_enum_mbus_code(struct v4l2_subdev *sd, 54062306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 54162306a36Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci if (code->index >= ARRAY_SIZE(s5pcsis_formats)) 54462306a36Sopenharmony_ci return -EINVAL; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci code->code = s5pcsis_formats[code->index].code; 54762306a36Sopenharmony_ci return 0; 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic struct csis_pix_format const *s5pcsis_try_format( 55162306a36Sopenharmony_ci struct v4l2_mbus_framefmt *mf) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci struct csis_pix_format const *csis_fmt; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci csis_fmt = find_csis_format(mf); 55662306a36Sopenharmony_ci if (csis_fmt == NULL) 55762306a36Sopenharmony_ci csis_fmt = &s5pcsis_formats[0]; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci mf->code = csis_fmt->code; 56062306a36Sopenharmony_ci v4l_bound_align_image(&mf->width, 1, CSIS_MAX_PIX_WIDTH, 56162306a36Sopenharmony_ci csis_fmt->pix_width_alignment, 56262306a36Sopenharmony_ci &mf->height, 1, CSIS_MAX_PIX_HEIGHT, 1, 56362306a36Sopenharmony_ci 0); 56462306a36Sopenharmony_ci return csis_fmt; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic struct v4l2_mbus_framefmt *__s5pcsis_get_format( 56862306a36Sopenharmony_ci struct csis_state *state, struct v4l2_subdev_state *sd_state, 56962306a36Sopenharmony_ci enum v4l2_subdev_format_whence which) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci if (which == V4L2_SUBDEV_FORMAT_TRY) 57262306a36Sopenharmony_ci return sd_state ? v4l2_subdev_get_try_format(&state->sd, 57362306a36Sopenharmony_ci sd_state, 0) : NULL; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci return &state->format; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic int s5pcsis_set_fmt(struct v4l2_subdev *sd, 57962306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 58062306a36Sopenharmony_ci struct v4l2_subdev_format *fmt) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci struct csis_state *state = sd_to_csis_state(sd); 58362306a36Sopenharmony_ci struct csis_pix_format const *csis_fmt; 58462306a36Sopenharmony_ci struct v4l2_mbus_framefmt *mf; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci mf = __s5pcsis_get_format(state, sd_state, fmt->which); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (fmt->pad == CSIS_PAD_SOURCE) { 58962306a36Sopenharmony_ci if (mf) { 59062306a36Sopenharmony_ci mutex_lock(&state->lock); 59162306a36Sopenharmony_ci fmt->format = *mf; 59262306a36Sopenharmony_ci mutex_unlock(&state->lock); 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci return 0; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci csis_fmt = s5pcsis_try_format(&fmt->format); 59762306a36Sopenharmony_ci if (mf) { 59862306a36Sopenharmony_ci mutex_lock(&state->lock); 59962306a36Sopenharmony_ci *mf = fmt->format; 60062306a36Sopenharmony_ci if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) 60162306a36Sopenharmony_ci state->csis_fmt = csis_fmt; 60262306a36Sopenharmony_ci mutex_unlock(&state->lock); 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci return 0; 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic int s5pcsis_get_fmt(struct v4l2_subdev *sd, 60862306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 60962306a36Sopenharmony_ci struct v4l2_subdev_format *fmt) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci struct csis_state *state = sd_to_csis_state(sd); 61262306a36Sopenharmony_ci struct v4l2_mbus_framefmt *mf; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci mf = __s5pcsis_get_format(state, sd_state, fmt->which); 61562306a36Sopenharmony_ci if (!mf) 61662306a36Sopenharmony_ci return -EINVAL; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci mutex_lock(&state->lock); 61962306a36Sopenharmony_ci fmt->format = *mf; 62062306a36Sopenharmony_ci mutex_unlock(&state->lock); 62162306a36Sopenharmony_ci return 0; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic int s5pcsis_s_rx_buffer(struct v4l2_subdev *sd, void *buf, 62562306a36Sopenharmony_ci unsigned int *size) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci struct csis_state *state = sd_to_csis_state(sd); 62862306a36Sopenharmony_ci unsigned long flags; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci *size = min_t(unsigned int, *size, S5PCSIS_PKTDATA_SIZE); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci spin_lock_irqsave(&state->slock, flags); 63362306a36Sopenharmony_ci state->pkt_buf.data = buf; 63462306a36Sopenharmony_ci state->pkt_buf.len = *size; 63562306a36Sopenharmony_ci spin_unlock_irqrestore(&state->slock, flags); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci return 0; 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic int s5pcsis_log_status(struct v4l2_subdev *sd) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci struct csis_state *state = sd_to_csis_state(sd); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci mutex_lock(&state->lock); 64562306a36Sopenharmony_ci s5pcsis_log_counters(state, true); 64662306a36Sopenharmony_ci if (debug && (state->flags & ST_POWERED)) 64762306a36Sopenharmony_ci dump_regs(state, __func__); 64862306a36Sopenharmony_ci mutex_unlock(&state->lock); 64962306a36Sopenharmony_ci return 0; 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cistatic const struct v4l2_subdev_core_ops s5pcsis_core_ops = { 65362306a36Sopenharmony_ci .s_power = s5pcsis_s_power, 65462306a36Sopenharmony_ci .log_status = s5pcsis_log_status, 65562306a36Sopenharmony_ci}; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_cistatic const struct v4l2_subdev_pad_ops s5pcsis_pad_ops = { 65862306a36Sopenharmony_ci .enum_mbus_code = s5pcsis_enum_mbus_code, 65962306a36Sopenharmony_ci .get_fmt = s5pcsis_get_fmt, 66062306a36Sopenharmony_ci .set_fmt = s5pcsis_set_fmt, 66162306a36Sopenharmony_ci}; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cistatic const struct v4l2_subdev_video_ops s5pcsis_video_ops = { 66462306a36Sopenharmony_ci .s_rx_buffer = s5pcsis_s_rx_buffer, 66562306a36Sopenharmony_ci .s_stream = s5pcsis_s_stream, 66662306a36Sopenharmony_ci}; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic const struct v4l2_subdev_ops s5pcsis_subdev_ops = { 66962306a36Sopenharmony_ci .core = &s5pcsis_core_ops, 67062306a36Sopenharmony_ci .pad = &s5pcsis_pad_ops, 67162306a36Sopenharmony_ci .video = &s5pcsis_video_ops, 67262306a36Sopenharmony_ci}; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cistatic irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci struct csis_state *state = dev_id; 67762306a36Sopenharmony_ci struct csis_pktbuf *pktbuf = &state->pkt_buf; 67862306a36Sopenharmony_ci unsigned long flags; 67962306a36Sopenharmony_ci u32 status; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci status = s5pcsis_read(state, S5PCSIS_INTSRC); 68262306a36Sopenharmony_ci spin_lock_irqsave(&state->slock, flags); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if ((status & S5PCSIS_INTSRC_NON_IMAGE_DATA) && pktbuf->data) { 68562306a36Sopenharmony_ci u32 offset; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (status & S5PCSIS_INTSRC_EVEN) 68862306a36Sopenharmony_ci offset = S5PCSIS_PKTDATA_EVEN; 68962306a36Sopenharmony_ci else 69062306a36Sopenharmony_ci offset = S5PCSIS_PKTDATA_ODD; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci memcpy(pktbuf->data, (u8 __force *)state->regs + offset, 69362306a36Sopenharmony_ci pktbuf->len); 69462306a36Sopenharmony_ci pktbuf->data = NULL; 69562306a36Sopenharmony_ci rmb(); 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci /* Update the event/error counters */ 69962306a36Sopenharmony_ci if ((status & S5PCSIS_INTSRC_ERRORS) || debug) { 70062306a36Sopenharmony_ci int i; 70162306a36Sopenharmony_ci for (i = 0; i < S5PCSIS_NUM_EVENTS; i++) { 70262306a36Sopenharmony_ci if (!(status & state->events[i].mask)) 70362306a36Sopenharmony_ci continue; 70462306a36Sopenharmony_ci state->events[i].counter++; 70562306a36Sopenharmony_ci v4l2_dbg(2, debug, &state->sd, "%s: %d\n", 70662306a36Sopenharmony_ci state->events[i].name, 70762306a36Sopenharmony_ci state->events[i].counter); 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci v4l2_dbg(2, debug, &state->sd, "status: %08x\n", status); 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci spin_unlock_irqrestore(&state->slock, flags); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci s5pcsis_write(state, S5PCSIS_INTSRC, status); 71462306a36Sopenharmony_ci return IRQ_HANDLED; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic int s5pcsis_parse_dt(struct platform_device *pdev, 71862306a36Sopenharmony_ci struct csis_state *state) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 72162306a36Sopenharmony_ci struct v4l2_fwnode_endpoint endpoint = { .bus_type = 0 }; 72262306a36Sopenharmony_ci int ret; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if (of_property_read_u32(node, "clock-frequency", 72562306a36Sopenharmony_ci &state->clk_frequency)) 72662306a36Sopenharmony_ci state->clk_frequency = DEFAULT_SCLK_CSIS_FREQ; 72762306a36Sopenharmony_ci if (of_property_read_u32(node, "bus-width", 72862306a36Sopenharmony_ci &state->max_num_lanes)) 72962306a36Sopenharmony_ci return -EINVAL; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci node = of_graph_get_next_endpoint(node, NULL); 73262306a36Sopenharmony_ci if (!node) { 73362306a36Sopenharmony_ci dev_err(&pdev->dev, "No port node at %pOF\n", 73462306a36Sopenharmony_ci pdev->dev.of_node); 73562306a36Sopenharmony_ci return -EINVAL; 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci /* Get port node and validate MIPI-CSI channel id. */ 73862306a36Sopenharmony_ci ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &endpoint); 73962306a36Sopenharmony_ci if (ret) 74062306a36Sopenharmony_ci goto err; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci state->index = endpoint.base.port - FIMC_INPUT_MIPI_CSI2_0; 74362306a36Sopenharmony_ci if (state->index >= CSIS_MAX_ENTITIES) { 74462306a36Sopenharmony_ci ret = -ENXIO; 74562306a36Sopenharmony_ci goto err; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci /* Get MIPI CSI-2 bus configuration from the endpoint node. */ 74962306a36Sopenharmony_ci of_property_read_u32(node, "samsung,csis-hs-settle", 75062306a36Sopenharmony_ci &state->hs_settle); 75162306a36Sopenharmony_ci state->wclk_ext = of_property_read_bool(node, 75262306a36Sopenharmony_ci "samsung,csis-wclk"); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci state->num_lanes = endpoint.bus.mipi_csi2.num_data_lanes; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cierr: 75762306a36Sopenharmony_ci of_node_put(node); 75862306a36Sopenharmony_ci return ret; 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_cistatic int s5pcsis_pm_resume(struct device *dev, bool runtime); 76262306a36Sopenharmony_cistatic const struct of_device_id s5pcsis_of_match[]; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_cistatic int s5pcsis_probe(struct platform_device *pdev) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci const struct of_device_id *of_id; 76762306a36Sopenharmony_ci const struct csis_drvdata *drv_data; 76862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 76962306a36Sopenharmony_ci struct csis_state *state; 77062306a36Sopenharmony_ci int ret = -ENOMEM; 77162306a36Sopenharmony_ci int i; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); 77462306a36Sopenharmony_ci if (!state) 77562306a36Sopenharmony_ci return -ENOMEM; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci mutex_init(&state->lock); 77862306a36Sopenharmony_ci spin_lock_init(&state->slock); 77962306a36Sopenharmony_ci state->pdev = pdev; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci of_id = of_match_node(s5pcsis_of_match, dev->of_node); 78262306a36Sopenharmony_ci if (WARN_ON(of_id == NULL)) 78362306a36Sopenharmony_ci return -EINVAL; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci drv_data = of_id->data; 78662306a36Sopenharmony_ci state->interrupt_mask = drv_data->interrupt_mask; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci ret = s5pcsis_parse_dt(pdev, state); 78962306a36Sopenharmony_ci if (ret < 0) 79062306a36Sopenharmony_ci return ret; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci if (state->num_lanes == 0 || state->num_lanes > state->max_num_lanes) { 79362306a36Sopenharmony_ci dev_err(dev, "Unsupported number of data lanes: %d (max. %d)\n", 79462306a36Sopenharmony_ci state->num_lanes, state->max_num_lanes); 79562306a36Sopenharmony_ci return -EINVAL; 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci state->phy = devm_phy_get(dev, "csis"); 79962306a36Sopenharmony_ci if (IS_ERR(state->phy)) 80062306a36Sopenharmony_ci return PTR_ERR(state->phy); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci state->regs = devm_platform_ioremap_resource(pdev, 0); 80362306a36Sopenharmony_ci if (IS_ERR(state->regs)) 80462306a36Sopenharmony_ci return PTR_ERR(state->regs); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci state->irq = platform_get_irq(pdev, 0); 80762306a36Sopenharmony_ci if (state->irq < 0) 80862306a36Sopenharmony_ci return state->irq; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci for (i = 0; i < CSIS_NUM_SUPPLIES; i++) 81162306a36Sopenharmony_ci state->supplies[i].supply = csis_supply_name[i]; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci ret = devm_regulator_bulk_get(dev, CSIS_NUM_SUPPLIES, 81462306a36Sopenharmony_ci state->supplies); 81562306a36Sopenharmony_ci if (ret) 81662306a36Sopenharmony_ci return ret; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci ret = s5pcsis_clk_get(state); 81962306a36Sopenharmony_ci if (ret < 0) 82062306a36Sopenharmony_ci return ret; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci if (state->clk_frequency) 82362306a36Sopenharmony_ci ret = clk_set_rate(state->clock[CSIS_CLK_MUX], 82462306a36Sopenharmony_ci state->clk_frequency); 82562306a36Sopenharmony_ci else 82662306a36Sopenharmony_ci dev_WARN(dev, "No clock frequency specified!\n"); 82762306a36Sopenharmony_ci if (ret < 0) 82862306a36Sopenharmony_ci goto e_clkput; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci ret = clk_enable(state->clock[CSIS_CLK_MUX]); 83162306a36Sopenharmony_ci if (ret < 0) 83262306a36Sopenharmony_ci goto e_clkput; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci ret = devm_request_irq(dev, state->irq, s5pcsis_irq_handler, 83562306a36Sopenharmony_ci 0, dev_name(dev), state); 83662306a36Sopenharmony_ci if (ret) { 83762306a36Sopenharmony_ci dev_err(dev, "Interrupt request failed\n"); 83862306a36Sopenharmony_ci goto e_clkdis; 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci v4l2_subdev_init(&state->sd, &s5pcsis_subdev_ops); 84262306a36Sopenharmony_ci state->sd.owner = THIS_MODULE; 84362306a36Sopenharmony_ci snprintf(state->sd.name, sizeof(state->sd.name), "%s.%d", 84462306a36Sopenharmony_ci CSIS_SUBDEV_NAME, state->index); 84562306a36Sopenharmony_ci state->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 84662306a36Sopenharmony_ci state->csis_fmt = &s5pcsis_formats[0]; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci state->format.code = s5pcsis_formats[0].code; 84962306a36Sopenharmony_ci state->format.width = S5PCSIS_DEF_PIX_WIDTH; 85062306a36Sopenharmony_ci state->format.height = S5PCSIS_DEF_PIX_HEIGHT; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci state->sd.entity.function = MEDIA_ENT_F_IO_V4L; 85362306a36Sopenharmony_ci state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 85462306a36Sopenharmony_ci state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; 85562306a36Sopenharmony_ci ret = media_entity_pads_init(&state->sd.entity, 85662306a36Sopenharmony_ci CSIS_PADS_NUM, state->pads); 85762306a36Sopenharmony_ci if (ret < 0) 85862306a36Sopenharmony_ci goto e_clkdis; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci /* This allows to retrieve the platform device id by the host driver */ 86162306a36Sopenharmony_ci v4l2_set_subdevdata(&state->sd, pdev); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci /* .. and a pointer to the subdev. */ 86462306a36Sopenharmony_ci platform_set_drvdata(pdev, &state->sd); 86562306a36Sopenharmony_ci memcpy(state->events, s5pcsis_events, sizeof(state->events)); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci pm_runtime_enable(dev); 86862306a36Sopenharmony_ci if (!pm_runtime_enabled(dev)) { 86962306a36Sopenharmony_ci ret = s5pcsis_pm_resume(dev, true); 87062306a36Sopenharmony_ci if (ret < 0) 87162306a36Sopenharmony_ci goto e_m_ent; 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci dev_info(&pdev->dev, "lanes: %d, hs_settle: %d, wclk: %d, freq: %u\n", 87562306a36Sopenharmony_ci state->num_lanes, state->hs_settle, state->wclk_ext, 87662306a36Sopenharmony_ci state->clk_frequency); 87762306a36Sopenharmony_ci return 0; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cie_m_ent: 88062306a36Sopenharmony_ci media_entity_cleanup(&state->sd.entity); 88162306a36Sopenharmony_cie_clkdis: 88262306a36Sopenharmony_ci clk_disable(state->clock[CSIS_CLK_MUX]); 88362306a36Sopenharmony_cie_clkput: 88462306a36Sopenharmony_ci s5pcsis_clk_put(state); 88562306a36Sopenharmony_ci return ret; 88662306a36Sopenharmony_ci} 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_cistatic int s5pcsis_pm_suspend(struct device *dev, bool runtime) 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci struct v4l2_subdev *sd = dev_get_drvdata(dev); 89162306a36Sopenharmony_ci struct csis_state *state = sd_to_csis_state(sd); 89262306a36Sopenharmony_ci int ret = 0; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: flags: 0x%x\n", 89562306a36Sopenharmony_ci __func__, state->flags); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci mutex_lock(&state->lock); 89862306a36Sopenharmony_ci if (state->flags & ST_POWERED) { 89962306a36Sopenharmony_ci s5pcsis_stop_stream(state); 90062306a36Sopenharmony_ci ret = phy_power_off(state->phy); 90162306a36Sopenharmony_ci if (ret) 90262306a36Sopenharmony_ci goto unlock; 90362306a36Sopenharmony_ci ret = regulator_bulk_disable(CSIS_NUM_SUPPLIES, 90462306a36Sopenharmony_ci state->supplies); 90562306a36Sopenharmony_ci if (ret) 90662306a36Sopenharmony_ci goto unlock; 90762306a36Sopenharmony_ci clk_disable(state->clock[CSIS_CLK_GATE]); 90862306a36Sopenharmony_ci state->flags &= ~ST_POWERED; 90962306a36Sopenharmony_ci if (!runtime) 91062306a36Sopenharmony_ci state->flags |= ST_SUSPENDED; 91162306a36Sopenharmony_ci } 91262306a36Sopenharmony_ci unlock: 91362306a36Sopenharmony_ci mutex_unlock(&state->lock); 91462306a36Sopenharmony_ci return ret ? -EAGAIN : 0; 91562306a36Sopenharmony_ci} 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_cistatic int s5pcsis_pm_resume(struct device *dev, bool runtime) 91862306a36Sopenharmony_ci{ 91962306a36Sopenharmony_ci struct v4l2_subdev *sd = dev_get_drvdata(dev); 92062306a36Sopenharmony_ci struct csis_state *state = sd_to_csis_state(sd); 92162306a36Sopenharmony_ci int ret = 0; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: flags: 0x%x\n", 92462306a36Sopenharmony_ci __func__, state->flags); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci mutex_lock(&state->lock); 92762306a36Sopenharmony_ci if (!runtime && !(state->flags & ST_SUSPENDED)) 92862306a36Sopenharmony_ci goto unlock; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci if (!(state->flags & ST_POWERED)) { 93162306a36Sopenharmony_ci ret = regulator_bulk_enable(CSIS_NUM_SUPPLIES, 93262306a36Sopenharmony_ci state->supplies); 93362306a36Sopenharmony_ci if (ret) 93462306a36Sopenharmony_ci goto unlock; 93562306a36Sopenharmony_ci ret = phy_power_on(state->phy); 93662306a36Sopenharmony_ci if (!ret) { 93762306a36Sopenharmony_ci state->flags |= ST_POWERED; 93862306a36Sopenharmony_ci } else { 93962306a36Sopenharmony_ci regulator_bulk_disable(CSIS_NUM_SUPPLIES, 94062306a36Sopenharmony_ci state->supplies); 94162306a36Sopenharmony_ci goto unlock; 94262306a36Sopenharmony_ci } 94362306a36Sopenharmony_ci clk_enable(state->clock[CSIS_CLK_GATE]); 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci if (state->flags & ST_STREAMING) 94662306a36Sopenharmony_ci s5pcsis_start_stream(state); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci state->flags &= ~ST_SUSPENDED; 94962306a36Sopenharmony_ci unlock: 95062306a36Sopenharmony_ci mutex_unlock(&state->lock); 95162306a36Sopenharmony_ci return ret ? -EAGAIN : 0; 95262306a36Sopenharmony_ci} 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 95562306a36Sopenharmony_cistatic int s5pcsis_suspend(struct device *dev) 95662306a36Sopenharmony_ci{ 95762306a36Sopenharmony_ci return s5pcsis_pm_suspend(dev, false); 95862306a36Sopenharmony_ci} 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_cistatic int s5pcsis_resume(struct device *dev) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci return s5pcsis_pm_resume(dev, false); 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci#endif 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci#ifdef CONFIG_PM 96762306a36Sopenharmony_cistatic int s5pcsis_runtime_suspend(struct device *dev) 96862306a36Sopenharmony_ci{ 96962306a36Sopenharmony_ci return s5pcsis_pm_suspend(dev, true); 97062306a36Sopenharmony_ci} 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_cistatic int s5pcsis_runtime_resume(struct device *dev) 97362306a36Sopenharmony_ci{ 97462306a36Sopenharmony_ci return s5pcsis_pm_resume(dev, true); 97562306a36Sopenharmony_ci} 97662306a36Sopenharmony_ci#endif 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_cistatic void s5pcsis_remove(struct platform_device *pdev) 97962306a36Sopenharmony_ci{ 98062306a36Sopenharmony_ci struct v4l2_subdev *sd = platform_get_drvdata(pdev); 98162306a36Sopenharmony_ci struct csis_state *state = sd_to_csis_state(sd); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 98462306a36Sopenharmony_ci s5pcsis_pm_suspend(&pdev->dev, true); 98562306a36Sopenharmony_ci clk_disable(state->clock[CSIS_CLK_MUX]); 98662306a36Sopenharmony_ci pm_runtime_set_suspended(&pdev->dev); 98762306a36Sopenharmony_ci s5pcsis_clk_put(state); 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci media_entity_cleanup(&state->sd.entity); 99062306a36Sopenharmony_ci} 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_cistatic const struct dev_pm_ops s5pcsis_pm_ops = { 99362306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(s5pcsis_runtime_suspend, s5pcsis_runtime_resume, 99462306a36Sopenharmony_ci NULL) 99562306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_suspend, s5pcsis_resume) 99662306a36Sopenharmony_ci}; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_cistatic const struct csis_drvdata exynos4_csis_drvdata = { 99962306a36Sopenharmony_ci .interrupt_mask = S5PCSIS_INTMSK_EXYNOS4_EN_ALL, 100062306a36Sopenharmony_ci}; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_cistatic const struct csis_drvdata exynos5_csis_drvdata = { 100362306a36Sopenharmony_ci .interrupt_mask = S5PCSIS_INTMSK_EXYNOS5_EN_ALL, 100462306a36Sopenharmony_ci}; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cistatic const struct of_device_id s5pcsis_of_match[] = { 100762306a36Sopenharmony_ci { 100862306a36Sopenharmony_ci .compatible = "samsung,s5pv210-csis", 100962306a36Sopenharmony_ci .data = &exynos4_csis_drvdata, 101062306a36Sopenharmony_ci }, { 101162306a36Sopenharmony_ci .compatible = "samsung,exynos4210-csis", 101262306a36Sopenharmony_ci .data = &exynos4_csis_drvdata, 101362306a36Sopenharmony_ci }, { 101462306a36Sopenharmony_ci .compatible = "samsung,exynos5250-csis", 101562306a36Sopenharmony_ci .data = &exynos5_csis_drvdata, 101662306a36Sopenharmony_ci }, 101762306a36Sopenharmony_ci { /* sentinel */ }, 101862306a36Sopenharmony_ci}; 101962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, s5pcsis_of_match); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_cistatic struct platform_driver s5pcsis_driver = { 102262306a36Sopenharmony_ci .probe = s5pcsis_probe, 102362306a36Sopenharmony_ci .remove_new = s5pcsis_remove, 102462306a36Sopenharmony_ci .driver = { 102562306a36Sopenharmony_ci .of_match_table = s5pcsis_of_match, 102662306a36Sopenharmony_ci .name = CSIS_DRIVER_NAME, 102762306a36Sopenharmony_ci .pm = &s5pcsis_pm_ops, 102862306a36Sopenharmony_ci }, 102962306a36Sopenharmony_ci}; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_cimodule_platform_driver(s5pcsis_driver); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ciMODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); 103462306a36Sopenharmony_ciMODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI-CSI2 receiver driver"); 103562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1036