From ba5bf51acf0075b193878a56ea3741982391da9c Mon Sep 17 00:00:00 2001 From: Vishal Sagar Date: Wed, 27 May 2020 15:57:19 +0200 Subject: media: v4l: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem driver The Xilinx MIPI CSI-2 Rx Subsystem soft IP is used to capture images from MIPI CSI-2 camera sensors and output AXI4-Stream video data ready for image processing. Please refer to PG232 for details. The CSI2 Rx controller filters out all packets except for the packets with data type fixed in hardware. RAW8 packets are always allowed to pass through. It is also used to setup and handle interrupts and enable the core. It logs all the events in respective counters between streaming on and off. The driver supports only the video format bridge enabled configuration. Some data types like YUV 422 10bpc, RAW16, RAW20 are supported when the CSI v2.0 feature is enabled in design. When the VCX feature is enabled, the maximum number of virtual channels becomes 16 from 4. Signed-off-by: Vishal Sagar Reviewed-by: Hyun Kwon Reviewed-by: Luca Ceresoli Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/xilinx/Kconfig | 7 + drivers/media/platform/xilinx/Makefile | 1 + drivers/media/platform/xilinx/xilinx-csi2rxss.c | 1111 +++++++++++++++++++++++ 3 files changed, 1119 insertions(+) create mode 100644 drivers/media/platform/xilinx/xilinx-csi2rxss.c (limited to 'drivers/media') diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig index 01c96fb66414..44587dccacf1 100644 --- a/drivers/media/platform/xilinx/Kconfig +++ b/drivers/media/platform/xilinx/Kconfig @@ -12,6 +12,13 @@ config VIDEO_XILINX if VIDEO_XILINX +config VIDEO_XILINX_CSI2RXSS + tristate "Xilinx CSI-2 Rx Subsystem" + help + Driver for Xilinx MIPI CSI-2 Rx Subsystem. This is a V4L sub-device + based driver that takes input from CSI-2 Tx source and converts + it into an AXI4-Stream. + config VIDEO_XILINX_TPG tristate "Xilinx Video Test Pattern Generator" depends on VIDEO_XILINX diff --git a/drivers/media/platform/xilinx/Makefile b/drivers/media/platform/xilinx/Makefile index 4cdc0b1ec7a5..6119a34f3043 100644 --- a/drivers/media/platform/xilinx/Makefile +++ b/drivers/media/platform/xilinx/Makefile @@ -3,5 +3,6 @@ xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.o +obj-$(CONFIG_VIDEO_XILINX_CSI2RXSS) += xilinx-csi2rxss.o obj-$(CONFIG_VIDEO_XILINX_TPG) += xilinx-tpg.o obj-$(CONFIG_VIDEO_XILINX_VTC) += xilinx-vtc.o diff --git a/drivers/media/platform/xilinx/xilinx-csi2rxss.c b/drivers/media/platform/xilinx/xilinx-csi2rxss.c new file mode 100644 index 000000000000..fff7ddec6745 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c @@ -0,0 +1,1111 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Xilinx MIPI CSI-2 Rx Subsystem + * + * Copyright (C) 2016 - 2020 Xilinx, Inc. + * + * Contacts: Vishal Sagar + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xilinx-vip.h" + +/* Register register map */ +#define XCSI_CCR_OFFSET 0x00 +#define XCSI_CCR_SOFTRESET BIT(1) +#define XCSI_CCR_ENABLE BIT(0) + +#define XCSI_PCR_OFFSET 0x04 +#define XCSI_PCR_MAXLANES_MASK GENMASK(4, 3) +#define XCSI_PCR_ACTLANES_MASK GENMASK(1, 0) + +#define XCSI_CSR_OFFSET 0x10 +#define XCSI_CSR_PKTCNT GENMASK(31, 16) +#define XCSI_CSR_SPFIFOFULL BIT(3) +#define XCSI_CSR_SPFIFONE BIT(2) +#define XCSI_CSR_SLBF BIT(1) +#define XCSI_CSR_RIPCD BIT(0) + +#define XCSI_GIER_OFFSET 0x20 +#define XCSI_GIER_GIE BIT(0) + +#define XCSI_ISR_OFFSET 0x24 +#define XCSI_IER_OFFSET 0x28 + +#define XCSI_ISR_FR BIT(31) +#define XCSI_ISR_VCXFE BIT(30) +#define XCSI_ISR_WCC BIT(22) +#define XCSI_ISR_ILC BIT(21) +#define XCSI_ISR_SPFIFOF BIT(20) +#define XCSI_ISR_SPFIFONE BIT(19) +#define XCSI_ISR_SLBF BIT(18) +#define XCSI_ISR_STOP BIT(17) +#define XCSI_ISR_SOTERR BIT(13) +#define XCSI_ISR_SOTSYNCERR BIT(12) +#define XCSI_ISR_ECC2BERR BIT(11) +#define XCSI_ISR_ECC1BERR BIT(10) +#define XCSI_ISR_CRCERR BIT(9) +#define XCSI_ISR_DATAIDERR BIT(8) +#define XCSI_ISR_VC3FSYNCERR BIT(7) +#define XCSI_ISR_VC3FLVLERR BIT(6) +#define XCSI_ISR_VC2FSYNCERR BIT(5) +#define XCSI_ISR_VC2FLVLERR BIT(4) +#define XCSI_ISR_VC1FSYNCERR BIT(3) +#define XCSI_ISR_VC1FLVLERR BIT(2) +#define XCSI_ISR_VC0FSYNCERR BIT(1) +#define XCSI_ISR_VC0FLVLERR BIT(0) + +#define XCSI_ISR_ALLINTR_MASK (0xc07e3fff) + +/* + * Removed VCXFE mask as it doesn't exist in IER + * Removed STOP state irq as this will keep driver in irq handler only + */ +#define XCSI_IER_INTR_MASK (XCSI_ISR_ALLINTR_MASK &\ + ~(XCSI_ISR_STOP | XCSI_ISR_VCXFE)) + +#define XCSI_SPKTR_OFFSET 0x30 +#define XCSI_SPKTR_DATA GENMASK(23, 8) +#define XCSI_SPKTR_VC GENMASK(7, 6) +#define XCSI_SPKTR_DT GENMASK(5, 0) +#define XCSI_SPKT_FIFO_DEPTH 31 + +#define XCSI_VCXR_OFFSET 0x34 +#define XCSI_VCXR_VCERR GENMASK(23, 0) +#define XCSI_VCXR_FSYNCERR BIT(1) +#define XCSI_VCXR_FLVLERR BIT(0) + +#define XCSI_CLKINFR_OFFSET 0x3C +#define XCSI_CLKINFR_STOP BIT(1) + +#define XCSI_DLXINFR_OFFSET 0x40 +#define XCSI_DLXINFR_STOP BIT(5) +#define XCSI_DLXINFR_SOTERR BIT(1) +#define XCSI_DLXINFR_SOTSYNCERR BIT(0) +#define XCSI_MAXDL_COUNT 0x4 + +#define XCSI_VCXINF1R_OFFSET 0x60 +#define XCSI_VCXINF1R_LINECOUNT GENMASK(31, 16) +#define XCSI_VCXINF1R_LINECOUNT_SHIFT 16 +#define XCSI_VCXINF1R_BYTECOUNT GENMASK(15, 0) + +#define XCSI_VCXINF2R_OFFSET 0x64 +#define XCSI_VCXINF2R_DT GENMASK(5, 0) +#define XCSI_MAXVCX_COUNT 16 + +/* + * Sink pad connected to sensor source pad. + * Source pad connected to next module like demosaic. + */ +#define XCSI_MEDIA_PADS 2 +#define XCSI_DEFAULT_WIDTH 1920 +#define XCSI_DEFAULT_HEIGHT 1080 + +/* MIPI CSI-2 Data Types from spec */ +#define XCSI_DT_YUV4228B 0x1e +#define XCSI_DT_YUV42210B 0x1f +#define XCSI_DT_RGB444 0x20 +#define XCSI_DT_RGB555 0x21 +#define XCSI_DT_RGB565 0x22 +#define XCSI_DT_RGB666 0x23 +#define XCSI_DT_RGB888 0x24 +#define XCSI_DT_RAW6 0x28 +#define XCSI_DT_RAW7 0x29 +#define XCSI_DT_RAW8 0x2a +#define XCSI_DT_RAW10 0x2b +#define XCSI_DT_RAW12 0x2c +#define XCSI_DT_RAW14 0x2d +#define XCSI_DT_RAW16 0x2e +#define XCSI_DT_RAW20 0x2f + +#define XCSI_VCX_START 4 +#define XCSI_MAX_VC 4 +#define XCSI_MAX_VCX 16 + +#define XCSI_NEXTREG_OFFSET 4 + +/* There are 2 events frame sync and frame level error per VC */ +#define XCSI_VCX_NUM_EVENTS ((XCSI_MAX_VCX - XCSI_MAX_VC) * 2) + +/** + * struct xcsi2rxss_event - Event log structure + * @mask: Event mask + * @name: Name of the event + */ +struct xcsi2rxss_event { + u32 mask; + const char *name; +}; + +static const struct xcsi2rxss_event xcsi2rxss_events[] = { + { XCSI_ISR_FR, "Frame Received" }, + { XCSI_ISR_VCXFE, "VCX Frame Errors" }, + { XCSI_ISR_WCC, "Word Count Errors" }, + { XCSI_ISR_ILC, "Invalid Lane Count Error" }, + { XCSI_ISR_SPFIFOF, "Short Packet FIFO OverFlow Error" }, + { XCSI_ISR_SPFIFONE, "Short Packet FIFO Not Empty" }, + { XCSI_ISR_SLBF, "Streamline Buffer Full Error" }, + { XCSI_ISR_STOP, "Lane Stop State" }, + { XCSI_ISR_SOTERR, "SOT Error" }, + { XCSI_ISR_SOTSYNCERR, "SOT Sync Error" }, + { XCSI_ISR_ECC2BERR, "2 Bit ECC Unrecoverable Error" }, + { XCSI_ISR_ECC1BERR, "1 Bit ECC Recoverable Error" }, + { XCSI_ISR_CRCERR, "CRC Error" }, + { XCSI_ISR_DATAIDERR, "Data Id Error" }, + { XCSI_ISR_VC3FSYNCERR, "Virtual Channel 3 Frame Sync Error" }, + { XCSI_ISR_VC3FLVLERR, "Virtual Channel 3 Frame Level Error" }, + { XCSI_ISR_VC2FSYNCERR, "Virtual Channel 2 Frame Sync Error" }, + { XCSI_ISR_VC2FLVLERR, "Virtual Channel 2 Frame Level Error" }, + { XCSI_ISR_VC1FSYNCERR, "Virtual Channel 1 Frame Sync Error" }, + { XCSI_ISR_VC1FLVLERR, "Virtual Channel 1 Frame Level Error" }, + { XCSI_ISR_VC0FSYNCERR, "Virtual Channel 0 Frame Sync Error" }, + { XCSI_ISR_VC0FLVLERR, "Virtual Channel 0 Frame Level Error" } +}; + +#define XCSI_NUM_EVENTS ARRAY_SIZE(xcsi2rxss_events) + +/* + * This table provides a mapping between CSI-2 Data type + * and media bus formats + */ +static const u32 xcsi2dt_mbus_lut[][2] = { + { XCSI_DT_YUV4228B, MEDIA_BUS_FMT_UYVY8_1X16 }, + { XCSI_DT_YUV42210B, MEDIA_BUS_FMT_UYVY10_1X20 }, + { XCSI_DT_RGB444, 0 }, + { XCSI_DT_RGB555, 0 }, + { XCSI_DT_RGB565, 0 }, + { XCSI_DT_RGB666, 0 }, + { XCSI_DT_RGB888, MEDIA_BUS_FMT_RBG888_1X24 }, + { XCSI_DT_RAW6, 0 }, + { XCSI_DT_RAW7, 0 }, + { XCSI_DT_RAW8, MEDIA_BUS_FMT_SRGGB8_1X8 }, + { XCSI_DT_RAW8, MEDIA_BUS_FMT_SBGGR8_1X8 }, + { XCSI_DT_RAW8, MEDIA_BUS_FMT_SGBRG8_1X8 }, + { XCSI_DT_RAW8, MEDIA_BUS_FMT_SGRBG8_1X8 }, + { XCSI_DT_RAW10, MEDIA_BUS_FMT_SRGGB10_1X10 }, + { XCSI_DT_RAW10, MEDIA_BUS_FMT_SBGGR10_1X10 }, + { XCSI_DT_RAW10, MEDIA_BUS_FMT_SGBRG10_1X10 }, + { XCSI_DT_RAW10, MEDIA_BUS_FMT_SGRBG10_1X10 }, + { XCSI_DT_RAW12, MEDIA_BUS_FMT_SRGGB12_1X12 }, + { XCSI_DT_RAW12, MEDIA_BUS_FMT_SBGGR12_1X12 }, + { XCSI_DT_RAW12, MEDIA_BUS_FMT_SGBRG12_1X12 }, + { XCSI_DT_RAW12, MEDIA_BUS_FMT_SGRBG12_1X12 }, + { XCSI_DT_RAW16, MEDIA_BUS_FMT_SRGGB16_1X16 }, + { XCSI_DT_RAW16, MEDIA_BUS_FMT_SBGGR16_1X16 }, + { XCSI_DT_RAW16, MEDIA_BUS_FMT_SGBRG16_1X16 }, + { XCSI_DT_RAW16, MEDIA_BUS_FMT_SGRBG16_1X16 }, + { XCSI_DT_RAW20, 0 }, +}; + +/** + * struct xcsi2rxss_state - CSI-2 Rx Subsystem device structure + * @subdev: The v4l2 subdev structure + * @format: Active V4L2 formats on each pad + * @default_format: Default V4L2 format + * @events: counter for events + * @vcx_events: counter for vcx_events + * @dev: Platform structure + * @rsubdev: Remote subdev connected to sink pad + * @rst_gpio: reset to video_aresetn + * @clks: array of clocks + * @iomem: Base address of subsystem + * @max_num_lanes: Maximum number of lanes present + * @datatype: Data type filter + * @lock: mutex for accessing this structure + * @pads: media pads + * @streaming: Flag for storing streaming state + * @enable_active_lanes: If number of active lanes can be modified + * @en_vcx: If more than 4 VC are enabled + * + * This structure contains the device driver related parameters + */ +struct xcsi2rxss_state { + struct v4l2_subdev subdev; + struct v4l2_mbus_framefmt format; + struct v4l2_mbus_framefmt default_format; + u32 events[XCSI_NUM_EVENTS]; + u32 vcx_events[XCSI_VCX_NUM_EVENTS]; + struct device *dev; + struct v4l2_subdev *rsubdev; + struct gpio_desc *rst_gpio; + struct clk_bulk_data *clks; + void __iomem *iomem; + u32 max_num_lanes; + u32 datatype; + /* used to protect access to this struct */ + struct mutex lock; + struct media_pad pads[XCSI_MEDIA_PADS]; + bool streaming; + bool enable_active_lanes; + bool en_vcx; +}; + +static const struct clk_bulk_data xcsi2rxss_clks[] = { + { .id = "lite_aclk" }, + { .id = "video_aclk" }, +}; + +static inline struct xcsi2rxss_state * +to_xcsi2rxssstate(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct xcsi2rxss_state, subdev); +} + +/* + * Register related operations + */ +static inline u32 xcsi2rxss_read(struct xcsi2rxss_state *xcsi2rxss, u32 addr) +{ + return ioread32(xcsi2rxss->iomem + addr); +} + +static inline void xcsi2rxss_write(struct xcsi2rxss_state *xcsi2rxss, u32 addr, + u32 value) +{ + iowrite32(value, xcsi2rxss->iomem + addr); +} + +static inline void xcsi2rxss_clr(struct xcsi2rxss_state *xcsi2rxss, u32 addr, + u32 clr) +{ + xcsi2rxss_write(xcsi2rxss, addr, + xcsi2rxss_read(xcsi2rxss, addr) & ~clr); +} + +static inline void xcsi2rxss_set(struct xcsi2rxss_state *xcsi2rxss, u32 addr, + u32 set) +{ + xcsi2rxss_write(xcsi2rxss, addr, xcsi2rxss_read(xcsi2rxss, addr) | set); +} + +/* + * This function returns the nth mbus for a data type. + * In case of error, mbus code returned is 0. + */ +static u32 xcsi2rxss_get_nth_mbus(u32 dt, u32 n) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(xcsi2dt_mbus_lut); i++) { + if (xcsi2dt_mbus_lut[i][0] == dt) { + if (n-- == 0) + return xcsi2dt_mbus_lut[i][1]; + } + } + + return 0; +} + +/* This returns the data type for a media bus format else 0 */ +static u32 xcsi2rxss_get_dt(u32 mbus) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(xcsi2dt_mbus_lut); i++) { + if (xcsi2dt_mbus_lut[i][1] == mbus) + return xcsi2dt_mbus_lut[i][0]; + } + + return 0; +} + +/** + * xcsi2rxss_soft_reset - Does a soft reset of the MIPI CSI-2 Rx Subsystem + * @state: Xilinx CSI-2 Rx Subsystem structure pointer + * + * Core takes less than 100 video clock cycles to reset. + * So a larger timeout value is chosen for margin. + * + * Return: 0 - on success OR -ETIME if reset times out + */ +static int xcsi2rxss_soft_reset(struct xcsi2rxss_state *state) +{ + u32 timeout = 1000; /* us */ + + xcsi2rxss_set(state, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET); + + while (xcsi2rxss_read(state, XCSI_CSR_OFFSET) & XCSI_CSR_RIPCD) { + if (timeout == 0) { + dev_err(state->dev, "soft reset timed out!\n"); + return -ETIME; + } + + timeout--; + udelay(1); + } + + xcsi2rxss_clr(state, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET); + return 0; +} + +static void xcsi2rxss_hard_reset(struct xcsi2rxss_state *state) +{ + if (!state->rst_gpio) + return; + + /* minimum of 40 dphy_clk_200M cycles */ + gpiod_set_value_cansleep(state->rst_gpio, 1); + usleep_range(1, 2); + gpiod_set_value_cansleep(state->rst_gpio, 0); +} + +static void xcsi2rxss_reset_event_counters(struct xcsi2rxss_state *state) +{ + unsigned int i; + + for (i = 0; i < XCSI_NUM_EVENTS; i++) + state->events[i] = 0; + + for (i = 0; i < XCSI_VCX_NUM_EVENTS; i++) + state->vcx_events[i] = 0; +} + +/* Print event counters */ +static void xcsi2rxss_log_counters(struct xcsi2rxss_state *state) +{ + struct device *dev = state->dev; + unsigned int i; + + for (i = 0; i < XCSI_NUM_EVENTS; i++) { + if (state->events[i] > 0) { + dev_info(dev, "%s events: %d\n", + xcsi2rxss_events[i].name, + state->events[i]); + } + } + + if (state->en_vcx) { + for (i = 0; i < XCSI_VCX_NUM_EVENTS; i++) { + if (state->vcx_events[i] > 0) { + dev_info(dev, + "VC %d Frame %s err vcx events: %d\n", + (i / 2) + XCSI_VCX_START, + i & 1 ? "Sync" : "Level", + state->vcx_events[i]); + } + } + } +} + +/** + * xcsi2rxss_log_status - Logs the status of the CSI-2 Receiver + * @sd: Pointer to V4L2 subdevice structure + * + * This function prints the current status of Xilinx MIPI CSI-2 + * + * Return: 0 on success + */ +static int xcsi2rxss_log_status(struct v4l2_subdev *sd) +{ + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); + struct device *dev = xcsi2rxss->dev; + u32 reg, data; + unsigned int i, max_vc; + + mutex_lock(&xcsi2rxss->lock); + + xcsi2rxss_log_counters(xcsi2rxss); + + dev_info(dev, "***** Core Status *****\n"); + data = xcsi2rxss_read(xcsi2rxss, XCSI_CSR_OFFSET); + dev_info(dev, "Short Packet FIFO Full = %s\n", + data & XCSI_CSR_SPFIFOFULL ? "true" : "false"); + dev_info(dev, "Short Packet FIFO Not Empty = %s\n", + data & XCSI_CSR_SPFIFONE ? "true" : "false"); + dev_info(dev, "Stream line buffer full = %s\n", + data & XCSI_CSR_SLBF ? "true" : "false"); + dev_info(dev, "Soft reset/Core disable in progress = %s\n", + data & XCSI_CSR_RIPCD ? "true" : "false"); + + /* Clk & Lane Info */ + dev_info(dev, "******** Clock Lane Info *********\n"); + data = xcsi2rxss_read(xcsi2rxss, XCSI_CLKINFR_OFFSET); + dev_info(dev, "Clock Lane in Stop State = %s\n", + data & XCSI_CLKINFR_STOP ? "true" : "false"); + + dev_info(dev, "******** Data Lane Info *********\n"); + dev_info(dev, "Lane\tSoT Error\tSoT Sync Error\tStop State\n"); + reg = XCSI_DLXINFR_OFFSET; + for (i = 0; i < XCSI_MAXDL_COUNT; i++) { + data = xcsi2rxss_read(xcsi2rxss, reg); + + dev_info(dev, "%d\t%s\t\t%s\t\t%s\n", i, + data & XCSI_DLXINFR_SOTERR ? "true" : "false", + data & XCSI_DLXINFR_SOTSYNCERR ? "true" : "false", + data & XCSI_DLXINFR_STOP ? "true" : "false"); + + reg += XCSI_NEXTREG_OFFSET; + } + + /* Virtual Channel Image Information */ + dev_info(dev, "********** Virtual Channel Info ************\n"); + dev_info(dev, "VC\tLine Count\tByte Count\tData Type\n"); + if (xcsi2rxss->en_vcx) + max_vc = XCSI_MAX_VCX; + else + max_vc = XCSI_MAX_VC; + + reg = XCSI_VCXINF1R_OFFSET; + for (i = 0; i < max_vc; i++) { + u32 line_count, byte_count, data_type; + + /* Get line and byte count from VCXINFR1 Register */ + data = xcsi2rxss_read(xcsi2rxss, reg); + byte_count = data & XCSI_VCXINF1R_BYTECOUNT; + line_count = data & XCSI_VCXINF1R_LINECOUNT; + line_count >>= XCSI_VCXINF1R_LINECOUNT_SHIFT; + + /* Get data type from VCXINFR2 Register */ + reg += XCSI_NEXTREG_OFFSET; + data = xcsi2rxss_read(xcsi2rxss, reg); + data_type = data & XCSI_VCXINF2R_DT; + + dev_info(dev, "%d\t%d\t\t%d\t\t0x%x\n", i, line_count, + byte_count, data_type); + + /* Move to next pair of VC Info registers */ + reg += XCSI_NEXTREG_OFFSET; + } + + mutex_unlock(&xcsi2rxss->lock); + + return 0; +} + +static struct v4l2_subdev *xcsi2rxss_get_remote_subdev(struct media_pad *local) +{ + struct media_pad *remote; + + remote = media_entity_remote_pad(local); + if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) + return NULL; + + return media_entity_to_v4l2_subdev(remote->entity); +} + +static int xcsi2rxss_start_stream(struct xcsi2rxss_state *state) +{ + int ret = 0; + + /* enable core */ + xcsi2rxss_set(state, XCSI_CCR_OFFSET, XCSI_CCR_ENABLE); + + ret = xcsi2rxss_soft_reset(state); + if (ret) { + state->streaming = false; + return ret; + } + + /* enable interrupts */ + xcsi2rxss_clr(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE); + xcsi2rxss_write(state, XCSI_IER_OFFSET, XCSI_IER_INTR_MASK); + xcsi2rxss_set(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE); + + state->streaming = true; + + state->rsubdev = + xcsi2rxss_get_remote_subdev(&state->pads[XVIP_PAD_SINK]); + + ret = v4l2_subdev_call(state->rsubdev, video, s_stream, 1); + if (ret) { + /* disable interrupts */ + xcsi2rxss_clr(state, XCSI_IER_OFFSET, XCSI_IER_INTR_MASK); + xcsi2rxss_clr(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE); + + /* disable core */ + xcsi2rxss_clr(state, XCSI_CCR_OFFSET, XCSI_CCR_ENABLE); + state->streaming = false; + } + + return ret; +} + +static void xcsi2rxss_stop_stream(struct xcsi2rxss_state *state) +{ + v4l2_subdev_call(state->rsubdev, video, s_stream, 0); + + /* disable interrupts */ + xcsi2rxss_clr(state, XCSI_IER_OFFSET, XCSI_IER_INTR_MASK); + xcsi2rxss_clr(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE); + + /* disable core */ + xcsi2rxss_clr(state, XCSI_CCR_OFFSET, XCSI_CCR_ENABLE); + state->streaming = false; +} + +/** + * xcsi2rxss_irq_handler - Interrupt handler for CSI-2 + * @irq: IRQ number + * @data: Pointer to device state + * + * In the interrupt handler, a list of event counters are updated for + * corresponding interrupts. This is useful to get status / debug. + * + * Return: IRQ_HANDLED after handling interrupts + */ +static irqreturn_t xcsi2rxss_irq_handler(int irq, void *data) +{ + struct xcsi2rxss_state *state = (struct xcsi2rxss_state *)data; + struct device *dev = state->dev; + u32 status; + + status = xcsi2rxss_read(state, XCSI_ISR_OFFSET) & XCSI_ISR_ALLINTR_MASK; + xcsi2rxss_write(state, XCSI_ISR_OFFSET, status); + + /* Received a short packet */ + if (status & XCSI_ISR_SPFIFONE) { + u32 count = 0; + + /* + * Drain generic short packet FIFO by reading max 31 + * (fifo depth) short packets from fifo or till fifo is empty. + */ + for (count = 0; count < XCSI_SPKT_FIFO_DEPTH; ++count) { + u32 spfifostat, spkt; + + spkt = xcsi2rxss_read(state, XCSI_SPKTR_OFFSET); + dev_dbg(dev, "Short packet = 0x%08x\n", spkt); + spfifostat = xcsi2rxss_read(state, XCSI_ISR_OFFSET); + spfifostat &= XCSI_ISR_SPFIFONE; + if (!spfifostat) + break; + xcsi2rxss_write(state, XCSI_ISR_OFFSET, spfifostat); + } + } + + /* Short packet FIFO overflow */ + if (status & XCSI_ISR_SPFIFOF) + dev_dbg_ratelimited(dev, "Short packet FIFO overflowed\n"); + + /* + * Stream line buffer full + * This means there is a backpressure from downstream IP + */ + if (status & XCSI_ISR_SLBF) { + dev_alert_ratelimited(dev, "Stream Line Buffer Full!\n"); + + /* disable interrupts */ + xcsi2rxss_clr(state, XCSI_IER_OFFSET, XCSI_IER_INTR_MASK); + xcsi2rxss_clr(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE); + + /* disable core */ + xcsi2rxss_clr(state, XCSI_CCR_OFFSET, XCSI_CCR_ENABLE); + + /* + * The IP needs to be hard reset before it can be used now. + * This will be done in streamoff. + */ + + /* + * TODO: Notify the whole pipeline with v4l2_subdev_notify() to + * inform userspace. + */ + } + + /* Increment event counters */ + if (status & XCSI_ISR_ALLINTR_MASK) { + unsigned int i; + + for (i = 0; i < XCSI_NUM_EVENTS; i++) { + if (!(status & xcsi2rxss_events[i].mask)) + continue; + state->events[i]++; + dev_dbg_ratelimited(dev, "%s: %u\n", + xcsi2rxss_events[i].name, + state->events[i]); + } + + if (status & XCSI_ISR_VCXFE && state->en_vcx) { + u32 vcxstatus; + + vcxstatus = xcsi2rxss_read(state, XCSI_VCXR_OFFSET); + vcxstatus &= XCSI_VCXR_VCERR; + for (i = 0; i < XCSI_VCX_NUM_EVENTS; i++) { + if (!(vcxstatus & BIT(i))) + continue; + state->vcx_events[i]++; + } + xcsi2rxss_write(state, XCSI_VCXR_OFFSET, vcxstatus); + } + } + + return IRQ_HANDLED; +} + +/** + * xcsi2rxss_s_stream - It is used to start/stop the streaming. + * @sd: V4L2 Sub device + * @enable: Flag (True / False) + * + * This function controls the start or stop of streaming for the + * Xilinx MIPI CSI-2 Rx Subsystem. + * + * Return: 0 on success, errors otherwise + */ +static int xcsi2rxss_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); + int ret = 0; + + mutex_lock(&xcsi2rxss->lock); + + if (enable == xcsi2rxss->streaming) + goto stream_done; + + if (enable) { + xcsi2rxss_reset_event_counters(xcsi2rxss); + ret = xcsi2rxss_start_stream(xcsi2rxss); + } else { + xcsi2rxss_stop_stream(xcsi2rxss); + xcsi2rxss_hard_reset(xcsi2rxss); + } + +stream_done: + mutex_unlock(&xcsi2rxss->lock); + return ret; +} + +static struct v4l2_mbus_framefmt * +__xcsi2rxss_get_pad_format(struct xcsi2rxss_state *xcsi2rxss, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&xcsi2rxss->subdev, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &xcsi2rxss->format; + default: + return NULL; + } +} + +/** + * xcsi2rxss_init_cfg - Initialise the pad format config to default + * @sd: Pointer to V4L2 Sub device structure + * @cfg: Pointer to sub device pad information structure + * + * This function is used to initialize the pad format with the default + * values. + * + * Return: 0 on success + */ +static int xcsi2rxss_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); + struct v4l2_mbus_framefmt *format; + unsigned int i; + + mutex_lock(&xcsi2rxss->lock); + for (i = 0; i < XCSI_MEDIA_PADS; i++) { + format = v4l2_subdev_get_try_format(sd, cfg, i); + *format = xcsi2rxss->default_format; + } + mutex_unlock(&xcsi2rxss->lock); + + return 0; +} + +/** + * xcsi2rxss_get_format - Get the pad format + * @sd: Pointer to V4L2 Sub device structure + * @cfg: Pointer to sub device pad information structure + * @fmt: Pointer to pad level media bus format + * + * This function is used to get the pad format information. + * + * Return: 0 on success + */ +static int xcsi2rxss_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); + + mutex_lock(&xcsi2rxss->lock); + fmt->format = *__xcsi2rxss_get_pad_format(xcsi2rxss, cfg, fmt->pad, + fmt->which); + mutex_unlock(&xcsi2rxss->lock); + + return 0; +} + +/** + * xcsi2rxss_set_format - This is used to set the pad format + * @sd: Pointer to V4L2 Sub device structure + * @cfg: Pointer to sub device pad information structure + * @fmt: Pointer to pad level media bus format + * + * This function is used to set the pad format. Since the pad format is fixed + * in hardware, it can't be modified on run time. So when a format set is + * requested by application, all parameters except the format type is saved + * for the pad and the original pad format is sent back to the application. + * + * Return: 0 on success + */ +static int xcsi2rxss_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); + struct v4l2_mbus_framefmt *__format; + u32 dt; + + mutex_lock(&xcsi2rxss->lock); + + /* + * Only the format->code parameter matters for CSI as the + * CSI format cannot be changed at runtime. + * Ensure that format to set is copied to over to CSI pad format + */ + __format = __xcsi2rxss_get_pad_format(xcsi2rxss, cfg, + fmt->pad, fmt->which); + + /* only sink pad format can be updated */ + if (fmt->pad == XVIP_PAD_SOURCE) { + fmt->format = *__format; + mutex_unlock(&xcsi2rxss->lock); + return 0; + } + + /* + * RAW8 is supported in all datatypes. So if requested media bus format + * is of RAW8 type, then allow to be set. In case core is configured to + * other RAW, YUV422 8/10 or RGB888, set appropriate media bus format. + */ + dt = xcsi2rxss_get_dt(fmt->format.code); + if (dt != xcsi2rxss->datatype && dt != XCSI_DT_RAW8) { + dev_dbg(xcsi2rxss->dev, "Unsupported media bus format"); + /* set the default format for the data type */ + fmt->format.code = xcsi2rxss_get_nth_mbus(xcsi2rxss->datatype, + 0); + } + + *__format = fmt->format; + mutex_unlock(&xcsi2rxss->lock); + + return 0; +} + +/* + * xcsi2rxss_enum_mbus_code - Handle pixel format enumeration + * @sd: pointer to v4l2 subdev structure + * @cfg: V4L2 subdev pad configuration + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * + * Return: -EINVAL or zero on success + */ +static int xcsi2rxss_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct xcsi2rxss_state *state = to_xcsi2rxssstate(sd); + u32 dt, n; + int ret = 0; + + /* RAW8 dt packets are available in all DT configurations */ + if (code->index < 4) { + n = code->index; + dt = XCSI_DT_RAW8; + } else if (state->datatype != XCSI_DT_RAW8) { + n = code->index - 4; + dt = state->datatype; + } else { + return -EINVAL; + } + + code->code = xcsi2rxss_get_nth_mbus(dt, n); + if (!code->code) + ret = -EINVAL; + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Media Operations + */ + +static const struct media_entity_operations xcsi2rxss_media_ops = { + .link_validate = v4l2_subdev_link_validate +}; + +static const struct v4l2_subdev_core_ops xcsi2rxss_core_ops = { + .log_status = xcsi2rxss_log_status, +}; + +static const struct v4l2_subdev_video_ops xcsi2rxss_video_ops = { + .s_stream = xcsi2rxss_s_stream +}; + +static const struct v4l2_subdev_pad_ops xcsi2rxss_pad_ops = { + .init_cfg = xcsi2rxss_init_cfg, + .get_fmt = xcsi2rxss_get_format, + .set_fmt = xcsi2rxss_set_format, + .enum_mbus_code = xcsi2rxss_enum_mbus_code, + .link_validate = v4l2_subdev_link_validate_default, +}; + +static const struct v4l2_subdev_ops xcsi2rxss_ops = { + .core = &xcsi2rxss_core_ops, + .video = &xcsi2rxss_video_ops, + .pad = &xcsi2rxss_pad_ops +}; + +static int xcsi2rxss_parse_of(struct xcsi2rxss_state *xcsi2rxss) +{ + struct device *dev = xcsi2rxss->dev; + struct device_node *node = dev->of_node; + + struct fwnode_handle *ep; + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + bool en_csi_v20, vfb; + int ret; + + en_csi_v20 = of_property_read_bool(node, "xlnx,en-csi-v2-0"); + if (en_csi_v20) + xcsi2rxss->en_vcx = of_property_read_bool(node, "xlnx,en-vcx"); + + xcsi2rxss->enable_active_lanes = + of_property_read_bool(node, "xlnx,en-active-lanes"); + + ret = of_property_read_u32(node, "xlnx,csi-pxl-format", + &xcsi2rxss->datatype); + if (ret < 0) { + dev_err(dev, "missing xlnx,csi-pxl-format property\n"); + return ret; + } + + switch (xcsi2rxss->datatype) { + case XCSI_DT_YUV4228B: + case XCSI_DT_RGB444: + case XCSI_DT_RGB555: + case XCSI_DT_RGB565: + case XCSI_DT_RGB666: + case XCSI_DT_RGB888: + case XCSI_DT_RAW6: + case XCSI_DT_RAW7: + case XCSI_DT_RAW8: + case XCSI_DT_RAW10: + case XCSI_DT_RAW12: + case XCSI_DT_RAW14: + break; + case XCSI_DT_YUV42210B: + case XCSI_DT_RAW16: + case XCSI_DT_RAW20: + if (!en_csi_v20) { + ret = -EINVAL; + dev_dbg(dev, "enable csi v2 for this pixel format"); + } + break; + default: + ret = -EINVAL; + } + if (ret < 0) { + dev_err(dev, "invalid csi-pxl-format property!\n"); + return ret; + } + + vfb = of_property_read_bool(node, "xlnx,vfb"); + if (!vfb) { + dev_err(dev, "operation without VFB is not supported\n"); + return -EINVAL; + } + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + XVIP_PAD_SINK, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep) { + dev_err(dev, "no sink port found"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_parse(ep, &vep); + fwnode_handle_put(ep); + if (ret) { + dev_err(dev, "error parsing sink port"); + return ret; + } + + dev_dbg(dev, "mipi number lanes = %d\n", + vep.bus.mipi_csi2.num_data_lanes); + + xcsi2rxss->max_num_lanes = vep.bus.mipi_csi2.num_data_lanes; + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + XVIP_PAD_SOURCE, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep) { + dev_err(dev, "no source port found"); + return -EINVAL; + } + + fwnode_handle_put(ep); + + dev_dbg(dev, "vcx %s, %u data lanes (%s), data type 0x%02x\n", + xcsi2rxss->en_vcx ? "enabled" : "disabled", + xcsi2rxss->max_num_lanes, + xcsi2rxss->enable_active_lanes ? "dynamic" : "static", + xcsi2rxss->datatype); + + return 0; +} + +static int xcsi2rxss_probe(struct platform_device *pdev) +{ + struct v4l2_subdev *subdev; + struct xcsi2rxss_state *xcsi2rxss; + int num_clks = ARRAY_SIZE(xcsi2rxss_clks); + struct device *dev = &pdev->dev; + int irq, ret; + + xcsi2rxss = devm_kzalloc(dev, sizeof(*xcsi2rxss), GFP_KERNEL); + if (!xcsi2rxss) + return -ENOMEM; + + xcsi2rxss->dev = dev; + + xcsi2rxss->clks = devm_kmemdup(dev, xcsi2rxss_clks, + sizeof(xcsi2rxss_clks), GFP_KERNEL); + if (!xcsi2rxss->clks) + return -ENOMEM; + + /* Reset GPIO */ + xcsi2rxss->rst_gpio = devm_gpiod_get_optional(dev, "video-reset", + GPIOD_OUT_HIGH); + if (IS_ERR(xcsi2rxss->rst_gpio)) { + if (PTR_ERR(xcsi2rxss->rst_gpio) != -EPROBE_DEFER) + dev_err(dev, "Video Reset GPIO not setup in DT"); + return PTR_ERR(xcsi2rxss->rst_gpio); + } + + ret = xcsi2rxss_parse_of(xcsi2rxss); + if (ret < 0) + return ret; + + xcsi2rxss->iomem = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(xcsi2rxss->iomem)) + return PTR_ERR(xcsi2rxss->iomem); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, + xcsi2rxss_irq_handler, IRQF_ONESHOT, + dev_name(dev), xcsi2rxss); + if (ret) { + dev_err(dev, "Err = %d Interrupt handler reg failed!\n", ret); + return ret; + } + + ret = clk_bulk_get(dev, num_clks, xcsi2rxss->clks); + if (ret) + return ret; + + /* TODO: Enable/disable clocks at stream on/off time. */ + ret = clk_bulk_prepare_enable(num_clks, xcsi2rxss->clks); + if (ret) + goto err_clk_put; + + mutex_init(&xcsi2rxss->lock); + + xcsi2rxss_hard_reset(xcsi2rxss); + xcsi2rxss_soft_reset(xcsi2rxss); + + /* Initialize V4L2 subdevice and media entity */ + xcsi2rxss->pads[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + xcsi2rxss->pads[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + /* Initialize the default format */ + xcsi2rxss->default_format.code = + xcsi2rxss_get_nth_mbus(xcsi2rxss->datatype, 0); + xcsi2rxss->default_format.field = V4L2_FIELD_NONE; + xcsi2rxss->default_format.colorspace = V4L2_COLORSPACE_SRGB; + xcsi2rxss->default_format.width = XCSI_DEFAULT_WIDTH; + xcsi2rxss->default_format.height = XCSI_DEFAULT_HEIGHT; + xcsi2rxss->format = xcsi2rxss->default_format; + + /* Initialize V4L2 subdevice and media entity */ + subdev = &xcsi2rxss->subdev; + v4l2_subdev_init(subdev, &xcsi2rxss_ops); + subdev->dev = dev; + strscpy(subdev->name, dev_name(dev), sizeof(subdev->name)); + subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; + subdev->entity.ops = &xcsi2rxss_media_ops; + v4l2_set_subdevdata(subdev, xcsi2rxss); + + ret = media_entity_pads_init(&subdev->entity, XCSI_MEDIA_PADS, + xcsi2rxss->pads); + if (ret < 0) + goto error; + + platform_set_drvdata(pdev, xcsi2rxss); + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) { + dev_err(dev, "failed to register subdev\n"); + goto error; + } + + return 0; +error: + media_entity_cleanup(&subdev->entity); + mutex_destroy(&xcsi2rxss->lock); + clk_bulk_disable_unprepare(num_clks, xcsi2rxss->clks); +err_clk_put: + clk_bulk_put(num_clks, xcsi2rxss->clks); + return ret; +} + +static int xcsi2rxss_remove(struct platform_device *pdev) +{ + struct xcsi2rxss_state *xcsi2rxss = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &xcsi2rxss->subdev; + int num_clks = ARRAY_SIZE(xcsi2rxss_clks); + + v4l2_async_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); + mutex_destroy(&xcsi2rxss->lock); + clk_bulk_disable_unprepare(num_clks, xcsi2rxss->clks); + clk_bulk_put(num_clks, xcsi2rxss->clks); + + return 0; +} + +static const struct of_device_id xcsi2rxss_of_id_table[] = { + { .compatible = "xlnx,mipi-csi2-rx-subsystem-5.0", }, + { } +}; +MODULE_DEVICE_TABLE(of, xcsi2rxss_of_id_table); + +static struct platform_driver xcsi2rxss_driver = { + .driver = { + .name = "xilinx-csi2rxss", + .of_match_table = xcsi2rxss_of_id_table, + }, + .probe = xcsi2rxss_probe, + .remove = xcsi2rxss_remove, +}; + +module_platform_driver(xcsi2rxss_driver); + +MODULE_AUTHOR("Vishal Sagar "); +MODULE_DESCRIPTION("Xilinx MIPI CSI-2 Rx Subsystem Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 157282a5c6273976b31cf4ba4b1c46663f6519d1 Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Thu, 21 May 2020 13:07:47 +0200 Subject: media: s5k6a3: Fix runtime PM imbalance on error pm_runtime_get() increments the runtime PM usage counter even when it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Signed-off-by: Dinghao Liu Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/s5k6a3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c index bc6cc5a558db..f26c168ef942 100644 --- a/drivers/media/i2c/s5k6a3.c +++ b/drivers/media/i2c/s5k6a3.c @@ -197,7 +197,7 @@ static int __s5k6a3_power_on(struct s5k6a3 *sensor) ret = pm_runtime_get(sensor->dev); if (ret < 0) - return ret; + goto error_rpm_put; ret = regulator_enable(sensor->supplies[i].consumer); if (ret < 0) -- cgit v1.2.3 From e40a8770fd147e7c0d5bd8c327d88090480242f2 Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Thu, 21 May 2020 12:22:04 +0200 Subject: media: fimc-capture: Fix runtime PM imbalance on error pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Signed-off-by: Dinghao Liu Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/fimc-capture.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index 705f182330ca..dd33b888971f 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -478,8 +478,10 @@ static int fimc_capture_open(struct file *file) set_bit(ST_CAPT_BUSY, &fimc->state); ret = pm_runtime_get_sync(&fimc->pdev->dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_sync(&fimc->pdev->dev); goto unlock; + } ret = v4l2_fh_open(file); if (ret) { -- cgit v1.2.3 From 90361693ad8d13a4311fc671903dcedd6ec06940 Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Thu, 21 May 2020 12:17:35 +0200 Subject: media: coda: Fix runtime PM imbalance on error pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Signed-off-by: Dinghao Liu Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-common.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 6f41f74d492c..3ab3d976d8ca 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -2653,7 +2653,7 @@ static int coda_open(struct file *file) ret = clk_prepare_enable(dev->clk_per); if (ret) - goto err_clk_per; + goto err_pm_get; ret = clk_prepare_enable(dev->clk_ahb); if (ret) @@ -2692,9 +2692,8 @@ err_ctx_init: clk_disable_unprepare(dev->clk_ahb); err_clk_ahb: clk_disable_unprepare(dev->clk_per); -err_clk_per: - pm_runtime_put_sync(dev->dev); err_pm_get: + pm_runtime_put_sync(dev->dev); v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); err_coda_name_init: -- cgit v1.2.3 From cf7b1c794d2f2362610143375b4156111e35aed5 Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Sat, 23 May 2020 09:36:39 +0200 Subject: media: camss: csid: Fix runtime PM imbalance in csid_set_power pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Signed-off-by: Dinghao Liu Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-csid.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index a5ae85674ffb..8a247b6f5550 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -562,8 +562,10 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) u32 hw_version; ret = pm_runtime_get_sync(dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_sync(dev); return ret; + } ret = regulator_enable(csid->vdda); if (ret < 0) { -- cgit v1.2.3 From 606578801afe6ec8bb03f5d494a4bae49a684ef3 Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Thu, 21 May 2020 14:06:21 +0200 Subject: media: camss: ispif: Fix runtime PM imbalance on error pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Signed-off-by: Dinghao Liu Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-ispif.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index 1f33b4eb198c..5722e971b184 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -344,8 +344,10 @@ static int ispif_set_power(struct v4l2_subdev *sd, int on) } ret = pm_runtime_get_sync(dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_sync(dev); goto exit; + } ret = camss_enable_clocks(ispif->nclocks, ispif->clock, dev); if (ret < 0) { -- cgit v1.2.3 From 779096916daede26699e75e51b0c3a000410d8ad Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Thu, 21 May 2020 13:56:07 +0200 Subject: media: camss: vfe: Fix runtime PM imbalance on error pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Signed-off-by: Dinghao Liu Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-vfe.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index a8c542fa647d..fc31c2c169cd 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -1265,12 +1265,12 @@ static int vfe_get(struct vfe_device *vfe) ret = vfe_set_clock_rates(vfe); if (ret < 0) - goto error_clocks; + goto error_pm_runtime_get; ret = camss_enable_clocks(vfe->nclocks, vfe->clock, vfe->camss->dev); if (ret < 0) - goto error_clocks; + goto error_pm_runtime_get; ret = vfe_reset(vfe); if (ret < 0) @@ -1282,7 +1282,7 @@ static int vfe_get(struct vfe_device *vfe) } else { ret = vfe_check_clock_rates(vfe); if (ret < 0) - goto error_clocks; + goto error_pm_runtime_get; } vfe->power_count++; @@ -1293,10 +1293,8 @@ static int vfe_get(struct vfe_device *vfe) error_reset: camss_disable_clocks(vfe->nclocks, vfe->clock); -error_clocks: - pm_runtime_put_sync(vfe->camss->dev); - error_pm_runtime_get: + pm_runtime_put_sync(vfe->camss->dev); camss_pm_domain_off(vfe->camss, vfe->id); error_pm_domain: -- cgit v1.2.3 From 00b0228061fa2dc0536fbf3355fedf2d447df3bd Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Sun, 24 May 2020 05:34:04 +0200 Subject: media: exynos4-is: Fix runtime PM imbalance in isp_video_open pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Signed-off-by: Dinghao Liu Reviewed-by: Sylwester Nawrocki Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/fimc-isp-video.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c index 15f443fa7208..612b9872afc8 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c @@ -293,6 +293,7 @@ static int isp_video_open(struct file *file) if (!ret) goto unlock; rel_fh: + pm_runtime_put_noidle(&isp->pdev->dev); v4l2_fh_release(file); unlock: mutex_unlock(&isp->video_lock); -- cgit v1.2.3 From 4f9b5709cc087337bf02f004f0a238ec7ebda25b Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Sun, 24 May 2020 04:59:02 +0200 Subject: media: exynos4-is: Fix runtime PM imbalance in fimc_is_probe pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. For all error paths after pm_runtime_get_sync(), things are the same. Signed-off-by: Dinghao Liu Reviewed-by: Sylwester Nawrocki Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/fimc-is.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index 64148b7e0d98..a474014f0a0f 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -871,6 +871,7 @@ err_dfs: err_sd: fimc_is_unregister_subdevs(is); err_pm: + pm_runtime_put_noidle(dev); if (!pm_runtime_enabled(dev)) fimc_is_runtime_suspend(dev); err_irq: -- cgit v1.2.3 From 4024d6f601e3c49bae9297930970f68d2621ea1d Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Sat, 23 May 2020 13:49:17 +0200 Subject: media: vpif: Fix runtime PM imbalance in vpif_probe When platform_get_resource() returns an error code, a pairing runtime PM usage counter decrement is needed to keep the counter balanced. Signed-off-by: Dinghao Liu Reviewed-by: Lad Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpif.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c index df66461f5d4f..f28c1b27eac6 100644 --- a/drivers/media/platform/davinci/vpif.c +++ b/drivers/media/platform/davinci/vpif.c @@ -458,6 +458,7 @@ static int vpif_probe(struct platform_device *pdev) res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res_irq) { dev_warn(&pdev->dev, "Missing IRQ resource.\n"); + pm_runtime_put(&pdev->dev); return -EINVAL; } -- cgit v1.2.3 From e1f60f426f5f57388ddd943f8bc49babc45d760c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sun, 24 May 2020 18:25:03 +0200 Subject: media: gspca: remove redundant assignment to variable retval The variable retval is being initialized with a value that is never read and it is being updated later with a new value. The initialization is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/jl2005bcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/gspca/jl2005bcd.c b/drivers/media/usb/gspca/jl2005bcd.c index 80ce7448b3dd..ca12f33f3e12 100644 --- a/drivers/media/usb/gspca/jl2005bcd.c +++ b/drivers/media/usb/gspca/jl2005bcd.c @@ -165,7 +165,7 @@ static int jl2005c_get_firmware_id(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *)gspca_dev; int i = 0; - int retval = -1; + int retval; unsigned char regs_to_read[] = {0x57, 0x02, 0x03, 0x5d, 0x5e, 0x5f}; gspca_dbg(gspca_dev, D_PROBE, "Running jl2005c_get_firmware_id\n"); -- cgit v1.2.3 From f5f5fa73fbfb9f346f1b5f37ebf343bae6ef6361 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:42 +0200 Subject: media: videobuf2: handle V4L2 buffer cache flags Set video buffer cache management flags corresponding to V4L2 cache flags. Both ->prepare() and ->finish() cache management hints should be passed during this stage (buffer preparation), because there is no other way for user-space to tell V4L2 to avoid ->finish() cache flush. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-v4l2.c | 48 +++++++++++++++++++++++++ include/media/videobuf2-core.h | 11 ++++++ 2 files changed, 59 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index eb5d5db96552..f13851212cc8 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -337,6 +337,53 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b return 0; } +static void set_buffer_cache_hints(struct vb2_queue *q, + struct vb2_buffer *vb, + struct v4l2_buffer *b) +{ + /* + * DMA exporter should take care of cache syncs, so we can avoid + * explicit ->prepare()/->finish() syncs. For other ->memory types + * we always need ->prepare() or/and ->finish() cache sync. + */ + if (q->memory == VB2_MEMORY_DMABUF) { + vb->need_cache_sync_on_finish = 0; + vb->need_cache_sync_on_prepare = 0; + return; + } + + /* + * Cache sync/invalidation flags are set by default in order to + * preserve existing behaviour for old apps/drivers. + */ + vb->need_cache_sync_on_prepare = 1; + vb->need_cache_sync_on_finish = 1; + + if (!vb2_queue_allows_cache_hints(q)) { + /* + * Clear buffer cache flags if queue does not support user + * space hints. That's to indicate to userspace that these + * flags won't work. + */ + b->flags &= ~V4L2_BUF_FLAG_NO_CACHE_INVALIDATE; + b->flags &= ~V4L2_BUF_FLAG_NO_CACHE_CLEAN; + return; + } + + /* + * ->finish() cache sync can be avoided when queue direction is + * TO_DEVICE. + */ + if (q->dma_dir == DMA_TO_DEVICE) + vb->need_cache_sync_on_finish = 0; + + if (b->flags & V4L2_BUF_FLAG_NO_CACHE_INVALIDATE) + vb->need_cache_sync_on_finish = 0; + + if (b->flags & V4L2_BUF_FLAG_NO_CACHE_CLEAN) + vb->need_cache_sync_on_prepare = 0; +} + static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *mdev, struct v4l2_buffer *b, bool is_prepare, struct media_request **p_req) @@ -381,6 +428,7 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md } if (!vb->prepared) { + set_buffer_cache_hints(q, vb, b); /* Copy relevant information provided by the userspace */ memset(vbuf->planes, 0, sizeof(vbuf->planes[0]) * vb->num_planes); diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 7f39d9fffc8c..ccc5c498d3e3 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -635,6 +635,17 @@ struct vb2_queue { #endif }; +/** + * vb2_queue_allows_cache_hints() - Return true if the queue allows cache + * and memory consistency hints. + * + * @q: pointer to &struct vb2_queue with videobuf2 queue + */ +static inline bool vb2_queue_allows_cache_hints(struct vb2_queue *q) +{ + return q->allow_cache_hints && q->memory == VB2_MEMORY_MMAP; +} + /** * vb2_plane_vaddr() - Return a kernel virtual address of a given plane. * @vb: pointer to &struct vb2_buffer to which the plane in -- cgit v1.2.3 From ac53503ee38a1ffbc47c7cca6cbfc48ba9c65c5e Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:43 +0200 Subject: media: videobuf2: add V4L2_FLAG_MEMORY_NON_CONSISTENT flag By setting or clearing V4L2_FLAG_MEMORY_NON_CONSISTENT flag user-space should be able to set or clear queue's NON_CONSISTENT ->dma_attrs. Queue's ->dma_attrs are passed to the underlying allocator in __vb2_buf_mem_alloc(), so thus user-space is able to request vb2 buffer's memory to be either consistent (coherent) or non-consistent. The patch set also adds a corresponding capability flag: fill_buf_caps() reports V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS when queue supports user-space cache management hints. Note, however, that MMAP_CACHE_HINTS capability only valid when the queue is used for memory MMAP-ed streaming I/O. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/userspace-api/media/v4l/buffer.rst | 40 ++++++++++++++++++++-- .../userspace-api/media/v4l/vidioc-reqbufs.rst | 10 ++++++ drivers/media/common/videobuf2/videobuf2-v4l2.c | 2 ++ include/uapi/linux/videodev2.h | 3 ++ 4 files changed, 53 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/Documentation/userspace-api/media/v4l/buffer.rst b/Documentation/userspace-api/media/v4l/buffer.rst index 951ae1ed485f..5088393b5a5c 100644 --- a/Documentation/userspace-api/media/v4l/buffer.rst +++ b/Documentation/userspace-api/media/v4l/buffer.rst @@ -577,7 +577,10 @@ Buffer Flags applications shall use this flag if the data captured in the buffer is not going to be touched by the CPU, instead the buffer will, probably, be passed on to a DMA-capable hardware unit for - further processing or output. + further processing or output. This flag is ignored unless the + queue is used for :ref:`memory mapping ` streaming I/O and + reports :ref:`V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS + ` capability. * .. _`V4L2-BUF-FLAG-NO-CACHE-CLEAN`: - ``V4L2_BUF_FLAG_NO_CACHE_CLEAN`` @@ -585,7 +588,10 @@ Buffer Flags - Caches do not have to be cleaned for this buffer. Typically applications shall use this flag for output buffers if the data in this buffer has not been created by the CPU but by some - DMA-capable unit, in which case caches have not been used. + DMA-capable unit, in which case caches have not been used. This flag + is ignored unless the queue is used for :ref:`memory mapping ` + streaming I/O and reports :ref:`V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS + ` capability. * .. _`V4L2-BUF-FLAG-M2M-HOLD-CAPTURE-BUF`: - ``V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF`` @@ -681,6 +687,36 @@ Buffer Flags \normalsize +.. _memory-flags: + +Memory Consistency Flags +======================== + +.. tabularcolumns:: |p{7.0cm}|p{2.2cm}|p{8.3cm}| + +.. cssclass:: longtable + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 3 1 4 + + * .. _`V4L2-FLAG-MEMORY-NON-CONSISTENT`: + + - ``V4L2_FLAG_MEMORY_NON_CONSISTENT`` + - 0x00000001 + - A buffer is allocated either in consistent (it will be automatically + coherent between the CPU and the bus) or non-consistent memory. The + latter can provide performance gains, for instance the CPU cache + sync/flush operations can be avoided if the buffer is accessed by the + corresponding device only and the CPU does not read/write to/from that + buffer. However, this requires extra care from the driver -- it must + guarantee memory consistency by issuing a cache flush/sync when + consistency is needed. If this flag is set V4L2 will attempt to + allocate the buffer in non-consistent memory. The flag takes effect + only if the buffer is used for :ref:`memory mapping ` I/O and the + queue reports the :ref:`V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS + ` capability. .. c:type:: v4l2_memory diff --git a/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst b/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst index b6d52083707b..96a59793d857 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst @@ -126,6 +126,7 @@ aborting or finishing any DMA in progress, an implicit .. _V4L2-BUF-CAP-SUPPORTS-REQUESTS: .. _V4L2-BUF-CAP-SUPPORTS-ORPHANED-BUFS: .. _V4L2-BUF-CAP-SUPPORTS-M2M-HOLD-CAPTURE-BUF: +.. _V4L2-BUF-CAP-SUPPORTS-MMAP-CACHE-HINTS: .. cssclass:: longtable @@ -156,6 +157,15 @@ aborting or finishing any DMA in progress, an implicit - Only valid for stateless decoders. If set, then userspace can set the ``V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF`` flag to hold off on returning the capture buffer until the OUTPUT timestamp changes. + * - ``V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS`` + - 0x00000040 + - This capability is set by the driver to indicate that the queue supports + cache and memory management hints. However, it's only valid when the + queue is used for :ref:`memory mapping ` streaming I/O. See + :ref:`V4L2_FLAG_MEMORY_NON_CONSISTENT `, + :ref:`V4L2_BUF_FLAG_NO_CACHE_INVALIDATE ` and + :ref:`V4L2_BUF_FLAG_NO_CACHE_CLEAN `. + Return Value ============ diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index f13851212cc8..e4b4354b42b8 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -710,6 +710,8 @@ static void fill_buf_caps(struct vb2_queue *q, u32 *caps) *caps |= V4L2_BUF_CAP_SUPPORTS_DMABUF; if (q->subsystem_flags & VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF) *caps |= V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF; + if (q->allow_cache_hints && q->io_modes & VB2_MMAP) + *caps |= V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS; #ifdef CONFIG_MEDIA_CONTROLLER_REQUEST_API if (q->supports_requests) *caps |= V4L2_BUF_CAP_SUPPORTS_REQUESTS; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index c3a1cf1c507f..34ba1017b89b 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -189,6 +189,8 @@ enum v4l2_memory { V4L2_MEMORY_DMABUF = 4, }; +#define V4L2_FLAG_MEMORY_NON_CONSISTENT (1 << 0) + /* see also http://vektor.theorem.ca/graphics/ycbcr/ */ enum v4l2_colorspace { /* @@ -954,6 +956,7 @@ struct v4l2_requestbuffers { #define V4L2_BUF_CAP_SUPPORTS_REQUESTS (1 << 3) #define V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS (1 << 4) #define V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF (1 << 5) +#define V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS (1 << 6) /** * struct v4l2_plane - plane info for multi-planar buffers -- cgit v1.2.3 From 7b4b45555c79db03dad8192e6ef85cb30236827b Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:44 +0200 Subject: media: videobuf2: add queue memory consistency parameter Preparations for future V4L2_FLAG_MEMORY_NON_CONSISTENT support. Extend vb2_core_reqbufs() parameters list to accept requests' ->flags, which will be used for memory consistency configuration. An attempt to allocate a buffer with consistency requirements which don't match queue's consistency model will fail. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 51 ++++++++++++++++++++----- drivers/media/common/videobuf2/videobuf2-v4l2.c | 6 +-- drivers/media/dvb-core/dvb_vb2.c | 2 +- include/media/videobuf2-core.h | 8 +++- 4 files changed, 51 insertions(+), 16 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 44d65f5be845..0fdcf90330df 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -664,11 +664,33 @@ int vb2_verify_memory_type(struct vb2_queue *q, } EXPORT_SYMBOL(vb2_verify_memory_type); +static void set_queue_consistency(struct vb2_queue *q, bool consistent_mem) +{ + q->dma_attrs &= ~DMA_ATTR_NON_CONSISTENT; + + if (!vb2_queue_allows_cache_hints(q)) + return; + if (!consistent_mem) + q->dma_attrs |= DMA_ATTR_NON_CONSISTENT; +} + +static bool verify_consistency_attr(struct vb2_queue *q, bool consistent_mem) +{ + bool queue_is_consistent = !(q->dma_attrs & DMA_ATTR_NON_CONSISTENT); + + if (consistent_mem != queue_is_consistent) { + dprintk(1, "memory consistency model mismatch\n"); + return false; + } + return true; +} + int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, - unsigned int *count) + unsigned int flags, unsigned int *count) { unsigned int num_buffers, allocated_buffers, num_planes = 0; unsigned plane_sizes[VB2_MAX_PLANES] = { }; + bool consistent_mem = true; unsigned int i; int ret; @@ -683,7 +705,8 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, } if (*count == 0 || q->num_buffers != 0 || - (q->memory != VB2_MEMORY_UNKNOWN && q->memory != memory)) { + (q->memory != VB2_MEMORY_UNKNOWN && q->memory != memory) || + !verify_consistency_attr(q, consistent_mem)) { /* * We already have buffers allocated, so first check if they * are not in use and can be freed. @@ -720,6 +743,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, num_buffers = min_t(unsigned int, num_buffers, VB2_MAX_FRAME); memset(q->alloc_devs, 0, sizeof(q->alloc_devs)); q->memory = memory; + set_queue_consistency(q, consistent_mem); /* * Ask the driver how many buffers and planes per buffer it requires. @@ -804,11 +828,13 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, EXPORT_SYMBOL_GPL(vb2_core_reqbufs); int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, - unsigned int *count, unsigned requested_planes, - const unsigned requested_sizes[]) + unsigned int flags, unsigned int *count, + unsigned int requested_planes, + const unsigned int requested_sizes[]) { unsigned int num_planes = 0, num_buffers, allocated_buffers; unsigned plane_sizes[VB2_MAX_PLANES] = { }; + bool consistent_mem = true; int ret; if (q->num_buffers == VB2_MAX_FRAME) { @@ -823,10 +849,15 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, } memset(q->alloc_devs, 0, sizeof(q->alloc_devs)); q->memory = memory; + set_queue_consistency(q, consistent_mem); q->waiting_for_buffers = !q->is_output; - } else if (q->memory != memory) { - dprintk(1, "memory model mismatch\n"); - return -EINVAL; + } else { + if (q->memory != memory) { + dprintk(1, "memory model mismatch\n"); + return -EINVAL; + } + if (!verify_consistency_attr(q, consistent_mem)) + return -EINVAL; } num_buffers = min(*count, VB2_MAX_FRAME - q->num_buffers); @@ -2498,7 +2529,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) fileio->memory = VB2_MEMORY_MMAP; fileio->type = q->type; q->fileio = fileio; - ret = vb2_core_reqbufs(q, fileio->memory, &fileio->count); + ret = vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count); if (ret) goto err_kfree; @@ -2555,7 +2586,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) err_reqbufs: fileio->count = 0; - vb2_core_reqbufs(q, fileio->memory, &fileio->count); + vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count); err_kfree: q->fileio = NULL; @@ -2575,7 +2606,7 @@ static int __vb2_cleanup_fileio(struct vb2_queue *q) vb2_core_streamoff(q, q->type); q->fileio = NULL; fileio->count = 0; - vb2_core_reqbufs(q, fileio->memory, &fileio->count); + vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count); kfree(fileio); dprintk(3, "file io emulator closed\n"); } diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index e4b4354b42b8..26a3ec333bb7 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -723,7 +723,7 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) int ret = vb2_verify_memory_type(q, req->memory, req->type); fill_buf_caps(q, &req->capabilities); - return ret ? ret : vb2_core_reqbufs(q, req->memory, &req->count); + return ret ? ret : vb2_core_reqbufs(q, req->memory, 0, &req->count); } EXPORT_SYMBOL_GPL(vb2_reqbufs); @@ -797,7 +797,7 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) for (i = 0; i < requested_planes; i++) if (requested_sizes[i] == 0) return -EINVAL; - return ret ? ret : vb2_core_create_bufs(q, create->memory, + return ret ? ret : vb2_core_create_bufs(q, create->memory, 0, &create->count, requested_planes, requested_sizes); } EXPORT_SYMBOL_GPL(vb2_create_bufs); @@ -973,7 +973,7 @@ int vb2_ioctl_reqbufs(struct file *file, void *priv, return res; if (vb2_queue_is_busy(vdev, file)) return -EBUSY; - res = vb2_core_reqbufs(vdev->queue, p->memory, &p->count); + res = vb2_core_reqbufs(vdev->queue, p->memory, 0, &p->count); /* If count == 0, then the owner has released all buffers and he is no longer owner of the queue. Otherwise we have a new owner. */ if (res == 0) diff --git a/drivers/media/dvb-core/dvb_vb2.c b/drivers/media/dvb-core/dvb_vb2.c index 6974f1731529..959d110407a4 100644 --- a/drivers/media/dvb-core/dvb_vb2.c +++ b/drivers/media/dvb-core/dvb_vb2.c @@ -342,7 +342,7 @@ int dvb_vb2_reqbufs(struct dvb_vb2_ctx *ctx, struct dmx_requestbuffers *req) ctx->buf_siz = req->size; ctx->buf_cnt = req->count; - ret = vb2_core_reqbufs(&ctx->vb_q, VB2_MEMORY_MMAP, &req->count); + ret = vb2_core_reqbufs(&ctx->vb_q, VB2_MEMORY_MMAP, 0, &req->count); if (ret) { ctx->state = DVB_VB2_STATE_NONE; dprintk(1, "[%s] count=%d size=%d errno=%d\n", ctx->name, diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index ccc5c498d3e3..9e68fe043a6c 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -740,6 +740,8 @@ void vb2_core_querybuf(struct vb2_queue *q, unsigned int index, void *pb); * vb2_core_reqbufs() - Initiate streaming. * @q: pointer to &struct vb2_queue with videobuf2 queue. * @memory: memory type, as defined by &enum vb2_memory. + * @flags: auxiliary queue/buffer management flags. Currently, the only + * used flag is %V4L2_FLAG_MEMORY_NON_CONSISTENT. * @count: requested buffer count. * * Videobuf2 core helper to implement VIDIOC_REQBUF() operation. It is called @@ -764,12 +766,13 @@ void vb2_core_querybuf(struct vb2_queue *q, unsigned int index, void *pb); * Return: returns zero on success; an error code otherwise. */ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, - unsigned int *count); + unsigned int flags, unsigned int *count); /** * vb2_core_create_bufs() - Allocate buffers and any required auxiliary structs * @q: pointer to &struct vb2_queue with videobuf2 queue. * @memory: memory type, as defined by &enum vb2_memory. + * @flags: auxiliary queue/buffer management flags. * @count: requested buffer count. * @requested_planes: number of planes requested. * @requested_sizes: array with the size of the planes. @@ -787,7 +790,8 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, * Return: returns zero on success; an error code otherwise. */ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, - unsigned int *count, unsigned int requested_planes, + unsigned int flags, unsigned int *count, + unsigned int requested_planes, const unsigned int requested_sizes[]); /** -- cgit v1.2.3 From 1e0b2318fa75d186ee0d2be31843ce867385fcc4 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:45 +0200 Subject: media: videobuf2: handle V4L2_FLAG_MEMORY_NON_CONSISTENT flag This patch lets user-space to request a non-consistent memory allocation during CREATE_BUFS and REQBUFS ioctl calls. = CREATE_BUFS struct v4l2_create_buffers has seven 4-byte reserved areas, so reserved[0] is renamed to ->flags. The struct, thus, now has six reserved 4-byte regions. = CREATE_BUFS32 struct v4l2_create_buffers32 has seven 4-byte reserved areas, so reserved[0] is renamed to ->flags. The struct, thus, now has six reserved 4-byte regions. = REQBUFS We use one bit of a ->reserved[1] member of struct v4l2_requestbuffers, which is now renamed to ->flags. Unlike v4l2_create_buffers, struct v4l2_requestbuffers does not have enough reserved room. Therefore for backward compatibility ->reserved and ->flags were put into anonymous union. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../userspace-api/media/v4l/vidioc-create-bufs.rst | 7 ++++++- .../userspace-api/media/v4l/vidioc-reqbufs.rst | 11 ++++++++-- drivers/media/common/videobuf2/videobuf2-core.c | 6 ++++++ drivers/media/common/videobuf2/videobuf2-v4l2.c | 24 ++++++++++++++++++---- drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 10 +++++++-- drivers/media/v4l2-core/v4l2-ioctl.c | 5 +---- include/uapi/linux/videodev2.h | 11 ++++++++-- 7 files changed, 59 insertions(+), 15 deletions(-) (limited to 'drivers/media') diff --git a/Documentation/userspace-api/media/v4l/vidioc-create-bufs.rst b/Documentation/userspace-api/media/v4l/vidioc-create-bufs.rst index e1afc5b504c2..f2a702870fad 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-create-bufs.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-create-bufs.rst @@ -121,7 +121,12 @@ than the number requested. other changes, then set ``count`` to 0, ``memory`` to ``V4L2_MEMORY_MMAP`` and ``format.type`` to the buffer type. * - __u32 - - ``reserved``\ [7] + - ``flags`` + - Specifies additional buffer management attributes. + See :ref:`memory-flags`. + + * - __u32 + - ``reserved``\ [6] - A place holder for future extensions. Drivers and applications must set the array to zero. diff --git a/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst b/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst index 96a59793d857..75d894d9c36c 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst @@ -112,10 +112,17 @@ aborting or finishing any DMA in progress, an implicit ``V4L2_MEMORY_MMAP`` and ``type`` set to the buffer type. This will free any previously allocated buffers, so this is typically something that will be done at the start of the application. + * - union { + - (anonymous) + * - __u32 + - ``flags`` + - Specifies additional buffer management attributes. + See :ref:`memory-flags`. * - __u32 - ``reserved``\ [1] - - A place holder for future extensions. Drivers and applications - must set the array to zero. + - Kept for backwards compatibility. Use ``flags`` instead. + * - } + - .. tabularcolumns:: |p{6.1cm}|p{2.2cm}|p{8.7cm}| diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 0fdcf90330df..626c4db5134c 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -694,6 +694,9 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, unsigned int i; int ret; + if (flags & V4L2_FLAG_MEMORY_NON_CONSISTENT) + consistent_mem = false; + if (q->streaming) { dprintk(1, "streaming active\n"); return -EBUSY; @@ -837,6 +840,9 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, bool consistent_mem = true; int ret; + if (flags & V4L2_FLAG_MEMORY_NON_CONSISTENT) + consistent_mem = false; + if (q->num_buffers == VB2_MAX_FRAME) { dprintk(1, "maximum number of buffers already allocated\n"); return -ENOBUFS; diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 26a3ec333bb7..559a229cac41 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -718,12 +718,22 @@ static void fill_buf_caps(struct vb2_queue *q, u32 *caps) #endif } +static void clear_consistency_attr(struct vb2_queue *q, + int memory, + unsigned int *flags) +{ + if (!q->allow_cache_hints || memory != V4L2_MEMORY_MMAP) + *flags &= ~V4L2_FLAG_MEMORY_NON_CONSISTENT; +} + int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) { int ret = vb2_verify_memory_type(q, req->memory, req->type); fill_buf_caps(q, &req->capabilities); - return ret ? ret : vb2_core_reqbufs(q, req->memory, 0, &req->count); + clear_consistency_attr(q, req->memory, &req->flags); + return ret ? ret : vb2_core_reqbufs(q, req->memory, + req->flags, &req->count); } EXPORT_SYMBOL_GPL(vb2_reqbufs); @@ -755,6 +765,7 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) unsigned i; fill_buf_caps(q, &create->capabilities); + clear_consistency_attr(q, create->memory, &create->flags); create->index = q->num_buffers; if (create->count == 0) return ret != -EBUSY ? ret : 0; @@ -797,8 +808,11 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) for (i = 0; i < requested_planes; i++) if (requested_sizes[i] == 0) return -EINVAL; - return ret ? ret : vb2_core_create_bufs(q, create->memory, 0, - &create->count, requested_planes, requested_sizes); + return ret ? ret : vb2_core_create_bufs(q, create->memory, + create->flags, + &create->count, + requested_planes, + requested_sizes); } EXPORT_SYMBOL_GPL(vb2_create_bufs); @@ -969,11 +983,12 @@ int vb2_ioctl_reqbufs(struct file *file, void *priv, int res = vb2_verify_memory_type(vdev->queue, p->memory, p->type); fill_buf_caps(vdev->queue, &p->capabilities); + clear_consistency_attr(vdev->queue, p->memory, &p->flags); if (res) return res; if (vb2_queue_is_busy(vdev, file)) return -EBUSY; - res = vb2_core_reqbufs(vdev->queue, p->memory, 0, &p->count); + res = vb2_core_reqbufs(vdev->queue, p->memory, p->flags, &p->count); /* If count == 0, then the owner has released all buffers and he is no longer owner of the queue. Otherwise we have a new owner. */ if (res == 0) @@ -991,6 +1006,7 @@ int vb2_ioctl_create_bufs(struct file *file, void *priv, p->index = vdev->queue->num_buffers; fill_buf_caps(vdev->queue, &p->capabilities); + clear_consistency_attr(vdev->queue, p->memory, &p->flags); /* * If count == 0, then just check if memory and type are valid. * Any -EBUSY result from vb2_verify_memory_type can be mapped to 0. diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index a99e82ec9ab6..593bcf6c3735 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -246,6 +246,9 @@ struct v4l2_format32 { * @memory: buffer memory type * @format: frame format, for which buffers are requested * @capabilities: capabilities of this buffer type. + * @flags: additional buffer management attributes (ignored unless the + * queue has V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS capability and + * configured for MMAP streaming I/O). * @reserved: future extensions */ struct v4l2_create_buffers32 { @@ -254,7 +257,8 @@ struct v4l2_create_buffers32 { __u32 memory; /* enum v4l2_memory */ struct v4l2_format32 format; __u32 capabilities; - __u32 reserved[7]; + __u32 flags; + __u32 reserved[6]; }; static int __bufsize_v4l2_format(struct v4l2_format32 __user *p32, u32 *size) @@ -355,7 +359,8 @@ static int get_v4l2_create32(struct v4l2_create_buffers __user *p64, { if (!access_ok(p32, sizeof(*p32)) || copy_in_user(p64, p32, - offsetof(struct v4l2_create_buffers32, format))) + offsetof(struct v4l2_create_buffers32, format)) || + assign_in_user(&p64->flags, &p32->flags)) return -EFAULT; return __get_v4l2_format32(&p64->format, &p32->format, aux_buf, aux_space); @@ -417,6 +422,7 @@ static int put_v4l2_create32(struct v4l2_create_buffers __user *p64, copy_in_user(p32, p64, offsetof(struct v4l2_create_buffers32, format)) || assign_in_user(&p32->capabilities, &p64->capabilities) || + assign_in_user(&p32->flags, &p64->flags) || copy_in_user(p32->reserved, p64->reserved, sizeof(p64->reserved))) return -EFAULT; return __put_v4l2_format32(&p64->format, &p32->format); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 2322f08a98be..02bfef0da76d 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2038,9 +2038,6 @@ static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops, if (ret) return ret; - - CLEAR_AFTER_FIELD(p, capabilities); - return ops->vidioc_reqbufs(file, fh, p); } @@ -2080,7 +2077,7 @@ static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops, if (ret) return ret; - CLEAR_AFTER_FIELD(create, capabilities); + CLEAR_AFTER_FIELD(create, flags); v4l_sanitize_format(&create->format); diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 34ba1017b89b..fec2607a07e3 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -946,7 +946,10 @@ struct v4l2_requestbuffers { __u32 type; /* enum v4l2_buf_type */ __u32 memory; /* enum v4l2_memory */ __u32 capabilities; - __u32 reserved[1]; + union { + __u32 flags; + __u32 reserved[1]; + }; }; /* capabilities for struct v4l2_requestbuffers and v4l2_create_buffers */ @@ -2450,6 +2453,9 @@ struct v4l2_dbg_chip_info { * @memory: enum v4l2_memory; buffer memory type * @format: frame format, for which buffers are requested * @capabilities: capabilities of this buffer type. + * @flags: additional buffer management attributes (ignored unless the + * queue has V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS capability + * and configured for MMAP streaming I/O). * @reserved: future extensions */ struct v4l2_create_buffers { @@ -2458,7 +2464,8 @@ struct v4l2_create_buffers { __u32 memory; struct v4l2_format format; __u32 capabilities; - __u32 reserved[7]; + __u32 flags; + __u32 reserved[6]; }; /* -- cgit v1.2.3 From 38a417e88aad1e5ad5ff2647d4cd26040b567fc3 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:46 +0200 Subject: media: videobuf2: factor out planes prepare/finish functions Factor out the code, no functional changes. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 52 +++++++++++++++---------- 1 file changed, 31 insertions(+), 21 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 626c4db5134c..1bf58e8c55e5 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -296,6 +296,32 @@ static void __vb2_buf_dmabuf_put(struct vb2_buffer *vb) __vb2_plane_dmabuf_put(vb, &vb->planes[plane]); } +/* + * __vb2_buf_mem_prepare() - call ->prepare() on buffer's private memory + * to sync caches + */ +static void __vb2_buf_mem_prepare(struct vb2_buffer *vb) +{ + unsigned int plane; + + for (plane = 0; plane < vb->num_planes; ++plane) + call_void_memop(vb, prepare, vb->planes[plane].mem_priv); + vb->synced = 1; +} + +/* + * __vb2_buf_mem_finish() - call ->finish on buffer's private memory + * to sync caches + */ +static void __vb2_buf_mem_finish(struct vb2_buffer *vb) +{ + unsigned int plane; + + for (plane = 0; plane < vb->num_planes; ++plane) + call_void_memop(vb, finish, vb->planes[plane].mem_priv); + vb->synced = 0; +} + /* * __setup_offsets() - setup unique offsets ("cookies") for every plane in * the buffer. @@ -959,7 +985,6 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) { struct vb2_queue *q = vb->vb2_queue; unsigned long flags; - unsigned int plane; if (WARN_ON(vb->state != VB2_BUF_STATE_ACTIVE)) return; @@ -979,12 +1004,8 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) dprintk(4, "done processing on buffer %d, state: %d\n", vb->index, state); - if (state != VB2_BUF_STATE_QUEUED) { - /* sync buffers */ - for (plane = 0; plane < vb->num_planes; ++plane) - call_void_memop(vb, finish, vb->planes[plane].mem_priv); - vb->synced = 0; - } + if (state != VB2_BUF_STATE_QUEUED) + __vb2_buf_mem_finish(vb); spin_lock_irqsave(&q->done_lock, flags); if (state == VB2_BUF_STATE_QUEUED) { @@ -1309,7 +1330,6 @@ static int __buf_prepare(struct vb2_buffer *vb) { struct vb2_queue *q = vb->vb2_queue; enum vb2_buffer_state orig_state = vb->state; - unsigned int plane; int ret; if (q->error) { @@ -1353,11 +1373,7 @@ static int __buf_prepare(struct vb2_buffer *vb) return ret; } - /* sync buffers */ - for (plane = 0; plane < vb->num_planes; ++plane) - call_void_memop(vb, prepare, vb->planes[plane].mem_priv); - - vb->synced = 1; + __vb2_buf_mem_prepare(vb); vb->prepared = 1; vb->state = orig_state; @@ -1977,14 +1993,8 @@ static void __vb2_queue_cancel(struct vb2_queue *q) call_void_vb_qop(vb, buf_request_complete, vb); } - if (vb->synced) { - unsigned int plane; - - for (plane = 0; plane < vb->num_planes; ++plane) - call_void_memop(vb, finish, - vb->planes[plane].mem_priv); - vb->synced = 0; - } + if (vb->synced) + __vb2_buf_mem_finish(vb); if (vb->prepared) { call_void_vb_qop(vb, buf_finish, vb); -- cgit v1.2.3 From dcf3bfe7c71d8da57cda2e3e06b8c28ed155741b Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:47 +0200 Subject: media: videobuf2: do not sync caches when we are allowed not to Skip ->prepare() or/and ->finish() cache synchronisation if user-space requested us to do so (or when queue dma direction permits us to skip cache syncs). Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 1bf58e8c55e5..b15fb11be846 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -304,8 +304,11 @@ static void __vb2_buf_mem_prepare(struct vb2_buffer *vb) { unsigned int plane; - for (plane = 0; plane < vb->num_planes; ++plane) - call_void_memop(vb, prepare, vb->planes[plane].mem_priv); + if (vb->need_cache_sync_on_prepare) { + for (plane = 0; plane < vb->num_planes; ++plane) + call_void_memop(vb, prepare, + vb->planes[plane].mem_priv); + } vb->synced = 1; } @@ -317,8 +320,11 @@ static void __vb2_buf_mem_finish(struct vb2_buffer *vb) { unsigned int plane; - for (plane = 0; plane < vb->num_planes; ++plane) - call_void_memop(vb, finish, vb->planes[plane].mem_priv); + if (vb->need_cache_sync_on_finish) { + for (plane = 0; plane < vb->num_planes; ++plane) + call_void_memop(vb, finish, + vb->planes[plane].mem_priv); + } vb->synced = 0; } -- cgit v1.2.3 From 093067ed1285d027d1e4a0e3dba72160849fbb84 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:48 +0200 Subject: media: videobuf2: check ->synced flag in prepare() and finish() This simplifies the code a tiny bit and let's us to avoid unneeded ->prepare()/->finish() calls. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index b15fb11be846..37d0186ba330 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -304,6 +304,9 @@ static void __vb2_buf_mem_prepare(struct vb2_buffer *vb) { unsigned int plane; + if (vb->synced) + return; + if (vb->need_cache_sync_on_prepare) { for (plane = 0; plane < vb->num_planes; ++plane) call_void_memop(vb, prepare, @@ -320,6 +323,9 @@ static void __vb2_buf_mem_finish(struct vb2_buffer *vb) { unsigned int plane; + if (!vb->synced) + return; + if (vb->need_cache_sync_on_finish) { for (plane = 0; plane < vb->num_planes; ++plane) call_void_memop(vb, finish, @@ -1999,8 +2005,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q) call_void_vb_qop(vb, buf_request_complete, vb); } - if (vb->synced) - __vb2_buf_mem_finish(vb); + __vb2_buf_mem_finish(vb); if (vb->prepared) { call_void_vb_qop(vb, buf_finish, vb); -- cgit v1.2.3 From d5adf1b0c2a31f8244ded805578188e067baaf5e Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:49 +0200 Subject: media: videobuf2: add begin/end cpu_access callbacks to dma-contig Provide begin_cpu_access() and end_cpu_access() callbacks for cache synchronisation on exported buffers. The patch also adds a new helper function - vb2_dc_buffer_consistent(), which returns true is if the buffer is consistent (DMA_ATTR_NON_CONSISTENT bit cleared), so then we don't need to sync anything. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/common/videobuf2/videobuf2-dma-contig.c | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/common/videobuf2/videobuf2-dma-contig.c b/drivers/media/common/videobuf2/videobuf2-dma-contig.c index f4b4a7c135eb..4c68263c617a 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-contig.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-contig.c @@ -42,6 +42,11 @@ struct vb2_dc_buf { struct dma_buf_attachment *db_attach; }; +static inline bool vb2_dc_buffer_consistent(unsigned long attr) +{ + return !(attr & DMA_ATTR_NON_CONSISTENT); +} + /*********************************************/ /* scatterlist table functions */ /*********************************************/ @@ -335,6 +340,34 @@ static void vb2_dc_dmabuf_ops_release(struct dma_buf *dbuf) vb2_dc_put(dbuf->priv); } +static int +vb2_dc_dmabuf_ops_begin_cpu_access(struct dma_buf *dbuf, + enum dma_data_direction direction) +{ + struct vb2_dc_buf *buf = dbuf->priv; + struct sg_table *sgt = buf->dma_sgt; + + if (vb2_dc_buffer_consistent(buf->attrs)) + return 0; + + dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); + return 0; +} + +static int +vb2_dc_dmabuf_ops_end_cpu_access(struct dma_buf *dbuf, + enum dma_data_direction direction) +{ + struct vb2_dc_buf *buf = dbuf->priv; + struct sg_table *sgt = buf->dma_sgt; + + if (vb2_dc_buffer_consistent(buf->attrs)) + return 0; + + dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); + return 0; +} + static void *vb2_dc_dmabuf_ops_vmap(struct dma_buf *dbuf) { struct vb2_dc_buf *buf = dbuf->priv; @@ -353,6 +386,8 @@ static const struct dma_buf_ops vb2_dc_dmabuf_ops = { .detach = vb2_dc_dmabuf_ops_detach, .map_dma_buf = vb2_dc_dmabuf_ops_map, .unmap_dma_buf = vb2_dc_dmabuf_ops_unmap, + .begin_cpu_access = vb2_dc_dmabuf_ops_begin_cpu_access, + .end_cpu_access = vb2_dc_dmabuf_ops_end_cpu_access, .vmap = vb2_dc_dmabuf_ops_vmap, .mmap = vb2_dc_dmabuf_ops_mmap, .release = vb2_dc_dmabuf_ops_release, -- cgit v1.2.3 From d4db5eb57cab049d378fbfb7ee842857009a8679 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:50 +0200 Subject: media: videobuf2: add begin/end cpu_access callbacks to dma-sg Provide begin_cpu_access() and end_cpu_access() dma_buf_ops callbacks for cache synchronisation on exported buffers. V4L2_FLAG_MEMORY_NON_CONSISTENT has no effect on dma-sg buffers. dma-sg allocates memory using the page allocator directly, so there is no memory consistency guarantee. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-dma-sg.c | 30 +++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/common/videobuf2/videobuf2-dma-sg.c b/drivers/media/common/videobuf2/videobuf2-dma-sg.c index 92072a08af25..595137e358e7 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-sg.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-sg.c @@ -120,6 +120,12 @@ static void *vb2_dma_sg_alloc(struct device *dev, unsigned long dma_attrs, buf->num_pages = size >> PAGE_SHIFT; buf->dma_sgt = &buf->sg_table; + /* + * NOTE: dma-sg allocates memory using the page allocator directly, so + * there is no memory consistency guarantee, hence dma-sg ignores DMA + * attributes passed from the upper layer. That means that + * V4L2_FLAG_MEMORY_NON_CONSISTENT has no effect on dma-sg buffers. + */ buf->pages = kvmalloc_array(buf->num_pages, sizeof(struct page *), GFP_KERNEL | __GFP_ZERO); if (!buf->pages) @@ -469,6 +475,28 @@ static void vb2_dma_sg_dmabuf_ops_release(struct dma_buf *dbuf) vb2_dma_sg_put(dbuf->priv); } +static int +vb2_dma_sg_dmabuf_ops_begin_cpu_access(struct dma_buf *dbuf, + enum dma_data_direction direction) +{ + struct vb2_dma_sg_buf *buf = dbuf->priv; + struct sg_table *sgt = buf->dma_sgt; + + dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); + return 0; +} + +static int +vb2_dma_sg_dmabuf_ops_end_cpu_access(struct dma_buf *dbuf, + enum dma_data_direction direction) +{ + struct vb2_dma_sg_buf *buf = dbuf->priv; + struct sg_table *sgt = buf->dma_sgt; + + dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); + return 0; +} + static void *vb2_dma_sg_dmabuf_ops_vmap(struct dma_buf *dbuf) { struct vb2_dma_sg_buf *buf = dbuf->priv; @@ -487,6 +515,8 @@ static const struct dma_buf_ops vb2_dma_sg_dmabuf_ops = { .detach = vb2_dma_sg_dmabuf_ops_detach, .map_dma_buf = vb2_dma_sg_dmabuf_ops_map, .unmap_dma_buf = vb2_dma_sg_dmabuf_ops_unmap, + .begin_cpu_access = vb2_dma_sg_dmabuf_ops_begin_cpu_access, + .end_cpu_access = vb2_dma_sg_dmabuf_ops_end_cpu_access, .vmap = vb2_dma_sg_dmabuf_ops_vmap, .mmap = vb2_dma_sg_dmabuf_ops_mmap, .release = vb2_dma_sg_dmabuf_ops_release, -- cgit v1.2.3 From a9a2c823601789e9f59ec75f13d0ef03138c04a4 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:51 +0200 Subject: media: videobuf2: don't test db_attach in dma-contig prepare and finish We moved cache management decision making to the upper layer and rely on buffer's need_cache_sync flags and videobuf2 core. If the upper layer (core) has decided to invoke ->prepare() or ->finish() then we must sync. For DMABUF ->need_cache_sync_on_prepare and ->need_cache_sync_on_flush are always false so videobuf core does not call ->prepare() and ->finish() on such buffers. Additionally, scratch the DMABUF comment. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-dma-contig.c | 6 ++---- drivers/media/common/videobuf2/videobuf2-dma-sg.c | 8 -------- 2 files changed, 2 insertions(+), 12 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/common/videobuf2/videobuf2-dma-contig.c b/drivers/media/common/videobuf2/videobuf2-dma-contig.c index 4c68263c617a..a753a03bc48e 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-contig.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-contig.c @@ -100,8 +100,7 @@ static void vb2_dc_prepare(void *buf_priv) struct vb2_dc_buf *buf = buf_priv; struct sg_table *sgt = buf->dma_sgt; - /* DMABUF exporter will flush the cache for us */ - if (!sgt || buf->db_attach) + if (!sgt) return; dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->orig_nents, @@ -113,8 +112,7 @@ static void vb2_dc_finish(void *buf_priv) struct vb2_dc_buf *buf = buf_priv; struct sg_table *sgt = buf->dma_sgt; - /* DMABUF exporter will flush the cache for us */ - if (!sgt || buf->db_attach) + if (!sgt) return; dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir); diff --git a/drivers/media/common/videobuf2/videobuf2-dma-sg.c b/drivers/media/common/videobuf2/videobuf2-dma-sg.c index 595137e358e7..0a40e00f0d7e 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-sg.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-sg.c @@ -204,10 +204,6 @@ static void vb2_dma_sg_prepare(void *buf_priv) struct vb2_dma_sg_buf *buf = buf_priv; struct sg_table *sgt = buf->dma_sgt; - /* DMABUF exporter will flush the cache for us */ - if (buf->db_attach) - return; - dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir); } @@ -217,10 +213,6 @@ static void vb2_dma_sg_finish(void *buf_priv) struct vb2_dma_sg_buf *buf = buf_priv; struct sg_table *sgt = buf->dma_sgt; - /* DMABUF exporter will flush the cache for us */ - if (buf->db_attach) - return; - dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir); } -- cgit v1.2.3 From 2ff99ca4bc30895f7e061f1fc0e510f19bd032bb Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:52 +0200 Subject: media: videobuf2: remove redundant if-statement That if-statement seems to be unneeded. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-dma-contig.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/common/videobuf2/videobuf2-dma-contig.c b/drivers/media/common/videobuf2/videobuf2-dma-contig.c index a753a03bc48e..ec3446cc45b8 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-contig.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-contig.c @@ -152,8 +152,7 @@ static void *vb2_dc_alloc(struct device *dev, unsigned long attrs, if (!buf) return ERR_PTR(-ENOMEM); - if (attrs) - buf->attrs = attrs; + buf->attrs = attrs; buf->cookie = dma_alloc_attrs(dev, size, &buf->dma_addr, GFP_KERNEL | gfp_flags, buf->attrs); if (!buf->cookie) { -- cgit v1.2.3 From 62a4cd0130f3936e431d5c8495af07edc9e1363d Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:53 +0200 Subject: media: vivid: add cache_hints module param Add a cache_hints module param to control per-queue user space cache hints support. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/admin-guide/media/vivid.rst | 9 +++++++++ drivers/media/test-drivers/vivid/vivid-core.c | 9 +++++++++ 2 files changed, 18 insertions(+) (limited to 'drivers/media') diff --git a/Documentation/admin-guide/media/vivid.rst b/Documentation/admin-guide/media/vivid.rst index 52e57b773f07..6d7175f96f74 100644 --- a/Documentation/admin-guide/media/vivid.rst +++ b/Documentation/admin-guide/media/vivid.rst @@ -293,6 +293,15 @@ all configurable using the following module options: - 0: vmalloc - 1: dma-contig +- cache_hints: + + specifies if the device should set queues' user-space cache and memory + consistency hint capability (V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS). + The hints are valid only when using MMAP streaming I/O. Default is 0. + + - 0: forbid hints + - 1: allow hints + Taken together, all these module options allow you to precisely customize the driver behavior and test your application with all sorts of permutations. It is also very suitable to emulate hardware that is not yet available, e.g. diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c index 6c740e3e6999..5c986df4a8d4 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.c +++ b/drivers/media/test-drivers/vivid/vivid-core.c @@ -169,6 +169,14 @@ MODULE_PARM_DESC(allocators, " memory allocator selection, default is 0.\n" "\t\t 0 == vmalloc\n" "\t\t 1 == dma-contig"); +static unsigned int cache_hints[VIVID_MAX_DEVS] = { + [0 ... (VIVID_MAX_DEVS - 1)] = 0 +}; +module_param_array(cache_hints, uint, NULL, 0444); +MODULE_PARM_DESC(cache_hints, " user-space cache hints, default is 0.\n" + "\t\t 0 == forbid\n" + "\t\t 1 == allow"); + static struct vivid_dev *vivid_devs[VIVID_MAX_DEVS]; const struct v4l2_rect vivid_min_rect = { @@ -819,6 +827,7 @@ static int vivid_create_queue(struct vivid_dev *dev, q->lock = &dev->mutex; q->dev = dev->v4l2_dev.dev; q->supports_requests = true; + q->allow_cache_hints = (cache_hints[dev->inst] == 1); return vb2_queue_init(q); } -- cgit v1.2.3 From 261463dbc34ff0acafe4d84df04535b48a15afea Mon Sep 17 00:00:00 2001 From: Sean Young Date: Sun, 3 May 2020 11:27:55 +0200 Subject: media: rc: add support for Infrared Toy and IR Droid devices http://dangerousprototypes.com/docs/USB_Infrared_Toy https://www.irdroid.com/irdroid-usb-ir-transceiver/ Reviewed-by: Greg Kroah-Hartman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/Kconfig | 11 + drivers/media/rc/Makefile | 1 + drivers/media/rc/ir_toy.c | 509 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 521 insertions(+) create mode 100644 drivers/media/rc/ir_toy.c (limited to 'drivers/media') diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index c18dee648253..2c0ee2e5b446 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -530,6 +530,17 @@ config IR_ZX To compile this driver as a module, choose M here: the module will be called zx-irdec. +config IR_TOY + tristate "Infrared Toy and IR Droid" + depends on RC_CORE + depends on USB_ARCH_HAS_HCD + help + Say Y here if you want to use the Infrared Toy or IR Droid, USB + versions. + + To compile this driver as a module, choose M here: the module will be + called ir_toy. + endif #RC_DEVICES endif #RC_CORE diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index 48d23433b3c0..5bb2932ab119 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -50,3 +50,4 @@ obj-$(CONFIG_IR_MTK) += mtk-cir.o obj-$(CONFIG_IR_ZX) += zx-irdec.o obj-$(CONFIG_IR_TANGO) += tango-ir.o obj-$(CONFIG_RC_XBOX_DVD) += xbox_remote.o +obj-$(CONFIG_IR_TOY) += ir_toy.o diff --git a/drivers/media/rc/ir_toy.c b/drivers/media/rc/ir_toy.c new file mode 100644 index 000000000000..5c7a7500a925 --- /dev/null +++ b/drivers/media/rc/ir_toy.c @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + * Infrared Toy and IR Droid RC core driver + * + * Copyright (C) 2020 Sean Young + + * This driver is based on the lirc driver which can be found here: + * https://sourceforge.net/p/lirc/git/ci/master/tree/plugins/irtoy.c + * Copyright (C) 2011 Peter Kooiman + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +static const u8 COMMAND_VERSION[] = { 'v' }; +// End transmit and repeat reset command so we exit sump mode +static const u8 COMMAND_RESET[] = { 0xff, 0xff, 0, 0, 0, 0, 0 }; +static const u8 COMMAND_SMODE_ENTER[] = { 's' }; +static const u8 COMMAND_TXSTART[] = { 0x26, 0x24, 0x25, 0x03 }; + +#define REPLY_XMITCOUNT 't' +#define REPLY_XMITSUCCESS 'C' +#define REPLY_VERSION 'V' +#define REPLY_SAMPLEMODEPROTO 'S' + +#define TIMEOUT 500 + +#define LEN_XMITRES 3 +#define LEN_VERSION 4 +#define LEN_SAMPLEMODEPROTO 3 + +#define MIN_FW_VERSION 20 +#define UNIT_NS 21333 +#define MAX_TIMEOUT_NS (UNIT_NS * U16_MAX) + +#define MAX_PACKET 64 + +enum state { + STATE_IRDATA, + STATE_RESET, + STATE_COMMAND, + STATE_TX, +}; + +struct irtoy { + struct device *dev; + struct usb_device *usbdev; + + struct rc_dev *rc; + struct urb *urb_in, *urb_out; + + u8 *in; + u8 *out; + struct completion command_done; + + bool pulse; + enum state state; + + void *tx_buf; + uint tx_len; + + uint emitted; + uint hw_version; + uint sw_version; + uint proto_version; + + char phys[64]; +}; + +static void irtoy_response(struct irtoy *irtoy, u32 len) +{ + switch (irtoy->state) { + case STATE_COMMAND: + if (len == LEN_VERSION && irtoy->in[0] == REPLY_VERSION) { + uint version; + + irtoy->in[LEN_VERSION] = 0; + + if (kstrtouint(irtoy->in + 1, 10, &version)) { + dev_err(irtoy->dev, "invalid version %*phN. Please make sure you are using firmware v20 or higher", + LEN_VERSION, irtoy->in); + break; + } + + dev_dbg(irtoy->dev, "version %s\n", irtoy->in); + + irtoy->hw_version = version / 100; + irtoy->sw_version = version % 100; + + irtoy->state = STATE_IRDATA; + complete(&irtoy->command_done); + } else if (len == LEN_SAMPLEMODEPROTO && + irtoy->in[0] == REPLY_SAMPLEMODEPROTO) { + uint version; + + irtoy->in[LEN_SAMPLEMODEPROTO] = 0; + + if (kstrtouint(irtoy->in + 1, 10, &version)) { + dev_err(irtoy->dev, "invalid sample mode response %*phN", + LEN_SAMPLEMODEPROTO, irtoy->in); + return; + } + + dev_dbg(irtoy->dev, "protocol %s\n", irtoy->in); + + irtoy->proto_version = version; + + irtoy->state = STATE_IRDATA; + complete(&irtoy->command_done); + } else { + dev_err(irtoy->dev, "unexpected response to command: %*phN\n", + len, irtoy->in); + } + break; + case STATE_IRDATA: { + struct ir_raw_event rawir = { .pulse = irtoy->pulse }; + __be16 *in = (__be16 *)irtoy->in; + int i; + + for (i = 0; i < len / sizeof(__be16); i++) { + u16 v = be16_to_cpu(in[i]); + + if (v == 0xffff) { + rawir.pulse = false; + } else { + rawir.duration = v * UNIT_NS; + ir_raw_event_store_with_timeout(irtoy->rc, + &rawir); + } + + rawir.pulse = !rawir.pulse; + } + + irtoy->pulse = rawir.pulse; + + ir_raw_event_handle(irtoy->rc); + break; + } + case STATE_TX: + if (irtoy->tx_len == 0) { + if (len == LEN_XMITRES && + irtoy->in[0] == REPLY_XMITCOUNT) { + u16 emitted = get_unaligned_be16(irtoy->in + 1); + + dev_dbg(irtoy->dev, "emitted:%u\n", emitted); + + irtoy->emitted = emitted; + } else if (len == 1 && + irtoy->in[0] == REPLY_XMITSUCCESS) { + irtoy->state = STATE_IRDATA; + complete(&irtoy->command_done); + } + } else { + // send next part of tx buffer + uint space = irtoy->in[0]; + uint buf_len; + int err; + + if (len != 1 || space > MAX_PACKET || space == 0) { + dev_err(irtoy->dev, "packet length expected: %*phN\n", + len, irtoy->in); + irtoy->state = STATE_IRDATA; + complete(&irtoy->command_done); + break; + } + + buf_len = min(space, irtoy->tx_len); + + dev_dbg(irtoy->dev, "remaining:%u sending:%u\n", + irtoy->tx_len, buf_len); + + memcpy(irtoy->out, irtoy->tx_buf, buf_len); + irtoy->urb_out->transfer_buffer_length = buf_len; + err = usb_submit_urb(irtoy->urb_out, GFP_ATOMIC); + if (err != 0) { + dev_err(irtoy->dev, "fail to submit tx buf urb: %d\n", + err); + irtoy->state = STATE_IRDATA; + complete(&irtoy->command_done); + break; + } + + irtoy->tx_buf += buf_len; + irtoy->tx_len -= buf_len; + } + break; + case STATE_RESET: + dev_err(irtoy->dev, "unexpected response to reset: %*phN\n", + len, irtoy->in); + } +} + +static void irtoy_out_callback(struct urb *urb) +{ + struct irtoy *irtoy = urb->context; + + if (urb->status == 0) { + if (irtoy->state == STATE_RESET) + complete(&irtoy->command_done); + } else { + dev_warn(irtoy->dev, "out urb status: %d\n", urb->status); + } +} + +static void irtoy_in_callback(struct urb *urb) +{ + struct irtoy *irtoy = urb->context; + int ret; + + if (urb->status == 0) + irtoy_response(irtoy, urb->actual_length); + else + dev_dbg(irtoy->dev, "in urb status: %d\n", urb->status); + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret && ret != -ENODEV) + dev_warn(irtoy->dev, "failed to resubmit urb: %d\n", ret); +} + +static int irtoy_command(struct irtoy *irtoy, const u8 *cmd, int cmd_len, + enum state state) +{ + int err; + + init_completion(&irtoy->command_done); + + irtoy->state = state; + + memcpy(irtoy->out, cmd, cmd_len); + irtoy->urb_out->transfer_buffer_length = cmd_len; + + err = usb_submit_urb(irtoy->urb_out, GFP_KERNEL); + if (err != 0) + return err; + + if (!wait_for_completion_timeout(&irtoy->command_done, + msecs_to_jiffies(TIMEOUT))) { + usb_kill_urb(irtoy->urb_out); + return -ETIMEDOUT; + } + + return 0; +} + +static int irtoy_setup(struct irtoy *irtoy) +{ + int err; + + err = irtoy_command(irtoy, COMMAND_RESET, sizeof(COMMAND_RESET), + STATE_RESET); + if (err != 0) { + dev_err(irtoy->dev, "could not write reset command: %d\n", + err); + return err; + } + + usleep_range(50, 50); + + // get version + err = irtoy_command(irtoy, COMMAND_VERSION, sizeof(COMMAND_VERSION), + STATE_COMMAND); + if (err) { + dev_err(irtoy->dev, "could not write version command: %d\n", + err); + return err; + } + + // enter sample mode + err = irtoy_command(irtoy, COMMAND_SMODE_ENTER, + sizeof(COMMAND_SMODE_ENTER), STATE_COMMAND); + if (err) + dev_err(irtoy->dev, "could not write sample command: %d\n", + err); + + return err; +} + +/* + * When sending IR, it is imperative that we send the IR data as quickly + * as possible to the device, so it does not run out of IR data and + * introduce gaps. Allocate the buffer here, and then feed the data from + * the urb callback handler. + */ +static int irtoy_tx(struct rc_dev *rc, uint *txbuf, uint count) +{ + struct irtoy *irtoy = rc->priv; + unsigned int i, size; + __be16 *buf; + int err; + + size = sizeof(u16) * (count + 1); + buf = kmalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (i = 0; i < count; i++) { + u16 v = DIV_ROUND_CLOSEST(US_TO_NS(txbuf[i]), UNIT_NS); + + if (!v) + v = 1; + buf[i] = cpu_to_be16(v); + } + + buf[count] = cpu_to_be16(0xffff); + + irtoy->tx_buf = buf; + irtoy->tx_len = size; + irtoy->emitted = 0; + + err = irtoy_command(irtoy, COMMAND_TXSTART, sizeof(COMMAND_TXSTART), + STATE_TX); + kfree(buf); + + if (err) { + dev_err(irtoy->dev, "failed to send tx start command: %d\n", + err); + // not sure what state the device is in, reset it + irtoy_setup(irtoy); + return err; + } + + if (size != irtoy->emitted) { + dev_err(irtoy->dev, "expected %u emitted, got %u\n", size, + irtoy->emitted); + // not sure what state the device is in, reset it + irtoy_setup(irtoy); + return -EINVAL; + } + + return count; +} + +static int irtoy_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_host_interface *idesc = intf->cur_altsetting; + struct usb_device *usbdev = interface_to_usbdev(intf); + struct usb_endpoint_descriptor *ep_in = NULL; + struct usb_endpoint_descriptor *ep_out = NULL; + struct usb_endpoint_descriptor *ep = NULL; + struct irtoy *irtoy; + struct rc_dev *rc; + struct urb *urb; + int i, pipe, err = -ENOMEM; + + for (i = 0; i < idesc->desc.bNumEndpoints; i++) { + ep = &idesc->endpoint[i].desc; + + if (!ep_in && usb_endpoint_is_bulk_in(ep) && + usb_endpoint_maxp(ep) == MAX_PACKET) + ep_in = ep; + + if (!ep_out && usb_endpoint_is_bulk_out(ep) && + usb_endpoint_maxp(ep) == MAX_PACKET) + ep_out = ep; + } + + if (!ep_in || !ep_out) { + dev_err(&intf->dev, "required endpoints not found\n"); + return -ENODEV; + } + + irtoy = kzalloc(sizeof(*irtoy), GFP_KERNEL); + if (!irtoy) + return -ENOMEM; + + irtoy->in = kmalloc(MAX_PACKET, GFP_KERNEL); + if (!irtoy->in) + goto free_irtoy; + + irtoy->out = kmalloc(MAX_PACKET, GFP_KERNEL); + if (!irtoy->out) + goto free_irtoy; + + rc = rc_allocate_device(RC_DRIVER_IR_RAW); + if (!rc) + goto free_irtoy; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + goto free_rcdev; + + pipe = usb_rcvbulkpipe(usbdev, ep_in->bEndpointAddress); + usb_fill_bulk_urb(urb, usbdev, pipe, irtoy->in, MAX_PACKET, + irtoy_in_callback, irtoy); + irtoy->urb_in = urb; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + goto free_rcdev; + + pipe = usb_sndbulkpipe(usbdev, ep_out->bEndpointAddress); + usb_fill_bulk_urb(urb, usbdev, pipe, irtoy->out, MAX_PACKET, + irtoy_out_callback, irtoy); + + irtoy->dev = &intf->dev; + irtoy->usbdev = usbdev; + irtoy->rc = rc; + irtoy->urb_out = urb; + irtoy->pulse = true; + + err = usb_submit_urb(irtoy->urb_in, GFP_KERNEL); + if (err != 0) { + dev_err(irtoy->dev, "fail to submit in urb: %d\n", err); + return err; + } + + err = irtoy_setup(irtoy); + if (err) + goto free_rcdev; + + dev_info(irtoy->dev, "version: hardware %u, firmware %u, protocol %u", + irtoy->hw_version, irtoy->sw_version, irtoy->proto_version); + + if (irtoy->sw_version < MIN_FW_VERSION) { + dev_err(irtoy->dev, "need firmware V%02u or higher", + MIN_FW_VERSION); + err = -ENODEV; + goto free_rcdev; + } + + usb_make_path(usbdev, irtoy->phys, sizeof(irtoy->phys)); + + rc->device_name = "Infrared Toy"; + rc->driver_name = KBUILD_MODNAME; + rc->input_phys = irtoy->phys; + usb_to_input_id(usbdev, &rc->input_id); + rc->dev.parent = &intf->dev; + rc->priv = irtoy; + rc->tx_ir = irtoy_tx; + rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; + rc->map_name = RC_MAP_RC6_MCE; + rc->rx_resolution = UNIT_NS; + rc->timeout = IR_DEFAULT_TIMEOUT; + + /* + * end of transmission is detected by absence of a usb packet + * with more pulse/spaces. However, each usb packet sent can + * contain 32 pulse/spaces, which can be quite lengthy, so there + * can be a delay between usb packets. For example with nec there is a + * 17ms gap between packets. + * + * So, make timeout a largish minimum which works with most protocols. + */ + rc->min_timeout = MS_TO_NS(40); + rc->max_timeout = MAX_TIMEOUT_NS; + + err = rc_register_device(rc); + if (err) + goto free_rcdev; + + usb_set_intfdata(intf, irtoy); + + return 0; + +free_rcdev: + usb_kill_urb(irtoy->urb_out); + usb_free_urb(irtoy->urb_out); + usb_kill_urb(irtoy->urb_in); + usb_free_urb(irtoy->urb_in); + rc_free_device(rc); +free_irtoy: + kfree(irtoy->in); + kfree(irtoy->out); + kfree(irtoy); + return err; +} + +static void irtoy_disconnect(struct usb_interface *intf) +{ + struct irtoy *ir = usb_get_intfdata(intf); + + rc_unregister_device(ir->rc); + usb_set_intfdata(intf, NULL); + usb_kill_urb(ir->urb_out); + usb_free_urb(ir->urb_out); + usb_kill_urb(ir->urb_in); + usb_free_urb(ir->urb_in); + kfree(ir->in); + kfree(ir->out); + kfree(ir); +} + +static const struct usb_device_id irtoy_table[] = { + { USB_DEVICE_INTERFACE_CLASS(0x04d8, 0xfd08, USB_CLASS_CDC_DATA) }, + { } +}; + +static struct usb_driver irtoy_driver = { + .name = KBUILD_MODNAME, + .probe = irtoy_probe, + .disconnect = irtoy_disconnect, + .id_table = irtoy_table, +}; + +module_usb_driver(irtoy_driver); + +MODULE_AUTHOR("Sean Young "); +MODULE_DESCRIPTION("Infrared Toy and IR Droid driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(usb, irtoy_table); -- cgit v1.2.3 From 1a55caf010c46d4f2073f9e92e97ef65358c16bf Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 7 May 2020 23:33:14 +0200 Subject: media: cxusb-analog: fix V4L2 dependency CONFIG_DVB_USB_CXUSB_ANALOG is a 'bool' symbol with a dependency on the tristate CONFIG_VIDEO_V4L2, which means it can be enabled as =y even when its dependency is =m. This leads to a link failure: drivers/media/usb/dvb-usb/cxusb-analog.o: In function `cxusb_medion_analog_init': cxusb-analog.c:(.text+0x92): undefined reference to `v4l2_subdev_call_wrappers' drivers/media/usb/dvb-usb/cxusb-analog.o: In function `cxusb_medion_register_analog': cxusb-analog.c:(.text+0x466): undefined reference to `v4l2_device_register' cxusb-analog.c:(.text+0x4c3): undefined reference to `v4l2_i2c_new_subdev' cxusb-analog.c:(.text+0x4fb): undefined reference to `v4l2_subdev_call_wrappers' ... Change the dependency only disallow the analog portion of the driver in that configuration. Fixes: e478d4054054 ("media: cxusb: add analog mode support for Medion MD95700") Signed-off-by: Arnd Bergmann Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig index 15d29c91662f..25ba03edcb5c 100644 --- a/drivers/media/usb/dvb-usb/Kconfig +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -151,6 +151,7 @@ config DVB_USB_CXUSB config DVB_USB_CXUSB_ANALOG bool "Analog support for the Conexant USB2.0 hybrid reference design" depends on DVB_USB_CXUSB && VIDEO_V4L2 + depends on VIDEO_V4L2=y || VIDEO_V4L2=DVB_USB_CXUSB select VIDEO_CX25840 select VIDEOBUF2_VMALLOC help -- cgit v1.2.3 From bc76369b89bbbbd01b2a3852287ea8092b59d012 Mon Sep 17 00:00:00 2001 From: Jan Pieter van Woerkom Date: Wed, 3 Jun 2020 05:58:40 +0200 Subject: media: dvbsky: MyGica T230* receivers also do DVB-C and DVB-T MyGica T230* receivers also do DVB-C and DVB-T: change their names. Signed-off-by: Jan Pieter van Woerkom Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/dvbsky.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c index 356fd8e66834..0d9657f7f29d 100644 --- a/drivers/media/usb/dvb-usb-v2/dvbsky.c +++ b/drivers/media/usb/dvb-usb-v2/dvbsky.c @@ -778,16 +778,16 @@ static const struct usb_device_id dvbsky_id_table[] = { &dvbsky_s960_props, "Terratec Cinergy S2 Rev.4", RC_MAP_DVBSKY) }, { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230, - &mygica_t230c_props, "MyGica Mini DVB-T2 USB Stick T230", + &mygica_t230c_props, "MyGica Mini DVB-(T/T2/C) USB Stick T230", RC_MAP_TOTAL_MEDIA_IN_HAND_02) }, { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230C, - &mygica_t230c_props, "MyGica Mini DVB-T2 USB Stick T230C", + &mygica_t230c_props, "MyGica Mini DVB-(T/T2/C) USB Stick T230C", RC_MAP_TOTAL_MEDIA_IN_HAND_02) }, { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230C_LITE, - &mygica_t230c_props, "MyGica Mini DVB-T2 USB Stick T230C Lite", + &mygica_t230c_props, "MyGica Mini DVB-(T/T2/C) USB Stick T230C Lite", NULL) }, { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230C2, - &mygica_t230c_props, "MyGica Mini DVB-T2 USB Stick T230C v2", + &mygica_t230c_props, "MyGica Mini DVB-(T/T2/C) USB Stick T230C v2", RC_MAP_TOTAL_MEDIA_IN_HAND_02) }, { } }; -- cgit v1.2.3 From 6499a0db9b0f1e903d52f8244eacc1d4be00eea2 Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Sat, 30 May 2020 16:42:08 +0200 Subject: media: pci: ttpci: av7110: fix possible buffer overflow caused by bad DMA value in debiirq() The value av7110->debi_virt is stored in DMA memory, and it is assigned to data, and thus data[0] can be modified at any time by malicious hardware. In this case, "if (data[0] < 2)" can be passed, but then data[0] can be changed into a large number, which may cause buffer overflow when the code "av7110->ci_slot[data[0]]" is used. To fix this possible bug, data[0] is assigned to a local variable, which replaces the use of data[0]. Signed-off-by: Jia-Ju Bai Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ttpci/av7110.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c index d0cdee1c6eb0..bf36b1e22b63 100644 --- a/drivers/media/pci/ttpci/av7110.c +++ b/drivers/media/pci/ttpci/av7110.c @@ -406,14 +406,15 @@ static void debiirq(unsigned long cookie) case DATA_CI_GET: { u8 *data = av7110->debi_virt; + u8 data_0 = data[0]; - if ((data[0] < 2) && data[2] == 0xff) { + if (data_0 < 2 && data[2] == 0xff) { int flags = 0; if (data[5] > 0) flags |= CA_CI_MODULE_PRESENT; if (data[5] > 5) flags |= CA_CI_MODULE_READY; - av7110->ci_slot[data[0]].flags = flags; + av7110->ci_slot[data_0].flags = flags; } else ci_get_data(&av7110->ci_rbuffer, av7110->debi_virt, -- cgit v1.2.3 From fc0456458df8b3421dba2a5508cd817fbc20ea71 Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Fri, 5 Jun 2020 18:17:28 +0200 Subject: media: budget-core: Improve exception handling in budget_register() budget_register() has no error handling after its failure. Add the missed undo functions for error handling to fix it. Signed-off-by: Chuhong Yuan Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ttpci/budget-core.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/ttpci/budget-core.c b/drivers/media/pci/ttpci/budget-core.c index fadbdeeb4495..293867b9e796 100644 --- a/drivers/media/pci/ttpci/budget-core.c +++ b/drivers/media/pci/ttpci/budget-core.c @@ -369,20 +369,25 @@ static int budget_register(struct budget *budget) ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->hw_frontend); if (ret < 0) - return ret; + goto err_release_dmx; budget->mem_frontend.source = DMX_MEMORY_FE; ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->mem_frontend); if (ret < 0) - return ret; + goto err_release_dmx; ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, &budget->hw_frontend); if (ret < 0) - return ret; + goto err_release_dmx; dvb_net_init(&budget->dvb_adapter, &budget->dvb_net, &dvbdemux->dmx); return 0; + +err_release_dmx: + dvb_dmxdev_release(&budget->dmxdev); + dvb_dmx_release(&budget->demux); + return ret; } static void budget_unregister(struct budget *budget) -- cgit v1.2.3 From 8dd65ed67e1679830dc8f94169d25ec9452da99d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 20 Jun 2020 12:16:40 +0200 Subject: media: cec: remove unused waitq and phys_addrs fields The cec_adapter struct contained a waitq field that isn't used anywhere, so drop this. It also contained a phys_addrs array to store any reported physical addresses. However, this was never actually used, so this field is removed as well. The original idea was to let the core keep track of this information, but nothing was ever done with this. Should this be needed in the future then it is easy enough to resurrect this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/core/cec-adap.c | 4 ---- drivers/media/cec/core/cec-core.c | 1 - include/media/cec.h | 2 -- 3 files changed, 7 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index 6a04d19a96b2..4efe8014445e 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -1306,7 +1306,6 @@ static int cec_config_log_addr(struct cec_adapter *adap, las->log_addr[idx] = log_addr; las->log_addr_mask |= 1 << log_addr; - adap->phys_addrs[log_addr] = adap->phys_addr; return 1; } @@ -1324,7 +1323,6 @@ static void cec_adap_unconfigure(struct cec_adapter *adap) adap->log_addrs.log_addr_mask = 0; adap->is_configuring = false; adap->is_configured = false; - memset(adap->phys_addrs, 0xff, sizeof(adap->phys_addrs)); cec_flush(adap); wake_up_interruptible(&adap->kthread_waitq); cec_post_state_event(adap); @@ -1974,8 +1972,6 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg, case CEC_MSG_REPORT_PHYSICAL_ADDR: { u16 pa = (msg->msg[2] << 8) | msg->msg[3]; - if (!from_unregistered) - adap->phys_addrs[init_laddr] = pa; dprintk(1, "reported physical address %x.%x.%x.%x for logical address %d\n", cec_phys_addr_exp(pa), init_laddr); break; diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index 0c52e1bb3910..c599cd94dd62 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -265,7 +265,6 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, adap->sequence = 0; adap->ops = ops; adap->priv = priv; - memset(adap->phys_addrs, 0xff, sizeof(adap->phys_addrs)); mutex_init(&adap->lock); INIT_LIST_HEAD(&adap->transmit_queue); INIT_LIST_HEAD(&adap->wait_queue); diff --git a/include/media/cec.h b/include/media/cec.h index 972bc8cd4384..1de44a4fc390 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -162,7 +162,6 @@ struct cec_adapter { struct task_struct *kthread; wait_queue_head_t kthread_waitq; - wait_queue_head_t waitq; const struct cec_adap_ops *ops; void *priv; @@ -197,7 +196,6 @@ struct cec_adapter { struct dentry *status_file; struct dentry *error_inj_file; - u16 phys_addrs[15]; u32 sequence; char input_phys[32]; -- cgit v1.2.3 From 52db11f38333f90dcdc9a12a09f9ec990516025d Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Mon, 25 May 2020 10:19:58 +0200 Subject: media: smiapp: Fix runtime PM imbalance on error When v4l2_async_register_subdev_sensor_common() returns an error code, a pairing runtime PM usage counter decrement is needed to keep the counter balanced. Signed-off-by: Dinghao Liu Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 5e4f6a2ef78e..0a8d27481bd1 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -3103,6 +3103,7 @@ static int smiapp_probe(struct i2c_client *client) return 0; out_disable_runtime_pm: + pm_runtime_put_noidle(&client->dev); pm_runtime_disable(&client->dev); out_media_entity_cleanup: -- cgit v1.2.3 From c1d7a2c8259c0a65189542d3e5d7a80f9ea012b6 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 16 Oct 2019 13:19:30 +0200 Subject: media: smiapp: Use pm_runtime_get_if_active Use the convenience function pm_runtime_get_if_active() instead of a number of calls to runtime PM to figure out if the device was already powered up. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 0a8d27481bd1..8a9c7de0c056 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -508,9 +508,7 @@ static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_get_noresume(&client->dev); - pm_status = pm_runtime_get_if_in_use(&client->dev); - pm_runtime_put_noidle(&client->dev); + pm_status = pm_runtime_get_if_active(&client->dev, true); if (!pm_status) return 0; -- cgit v1.2.3 From 1e8d3bbcdda30973a10d34032401647fc4111747 Mon Sep 17 00:00:00 2001 From: Bingbu Cao Date: Thu, 4 Jun 2020 03:25:19 +0200 Subject: media: ov2740: make ov2740 driver only work with ACPI As the ACPI id of ov2740 camera sensor was registered as "INT3474", current ov2740 driver is supposed to be working with ACPI only. Signed-off-by: Bingbu Cao Suggested-by: Andy Shevchenko Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 1 + drivers/media/i2c/ov2740.c | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index da11036ad804..6804ad57ee71 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -860,6 +860,7 @@ config VIDEO_OV2685 config VIDEO_OV2740 tristate "OmniVision OV2740 sensor support" depends on VIDEO_V4L2 && I2C + depends on ACPI || COMPILE_TEST select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index 2dd2609db873..33025c6d23ac 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -988,20 +988,18 @@ static const struct dev_pm_ops ov2740_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(ov2740_suspend, ov2740_resume) }; -#ifdef CONFIG_ACPI static const struct acpi_device_id ov2740_acpi_ids[] = { {"INT3474"}, {} }; MODULE_DEVICE_TABLE(acpi, ov2740_acpi_ids); -#endif static struct i2c_driver ov2740_i2c_driver = { .driver = { .name = "ov2740", .pm = &ov2740_pm_ops, - .acpi_match_table = ACPI_PTR(ov2740_acpi_ids), + .acpi_match_table = ov2740_acpi_ids, }, .probe_new = ov2740_probe, .remove = ov2740_remove, -- cgit v1.2.3 From 4603a5b4a87ccd6fb90cbfa10195291cfcf6ba34 Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Wed, 3 Jun 2020 18:40:48 +0200 Subject: media: marvell-ccic: Add missed v4l2_async_notifier_cleanup() mccic_register() forgets to cleanup the notifier in its error handler. mccic_shutdown() also misses calling v4l2_async_notifier_cleanup(). Add the missed calls to fix them. Fixes: 3eefe36cc00c ("media: marvell-ccic: use async notifier to get the sensor") Signed-off-by: Chuhong Yuan Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/marvell-ccic/mcam-core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 09775b6624c6..326e79b8531c 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -1940,6 +1940,7 @@ int mccic_register(struct mcam_camera *cam) out: v4l2_async_notifier_unregister(&cam->notifier); v4l2_device_unregister(&cam->v4l2_dev); + v4l2_async_notifier_cleanup(&cam->notifier); return ret; } EXPORT_SYMBOL_GPL(mccic_register); @@ -1961,6 +1962,7 @@ void mccic_shutdown(struct mcam_camera *cam) v4l2_ctrl_handler_free(&cam->ctrl_handler); v4l2_async_notifier_unregister(&cam->notifier); v4l2_device_unregister(&cam->v4l2_dev); + v4l2_async_notifier_cleanup(&cam->notifier); } EXPORT_SYMBOL_GPL(mccic_shutdown); -- cgit v1.2.3 From dc7690a73017e1236202022e26a6aa133f239c8c Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Wed, 3 Jun 2020 18:41:22 +0200 Subject: media: omap3isp: Add missed v4l2_ctrl_handler_free() for preview_init_entities() preview_init_entities() does not call v4l2_ctrl_handler_free() when it fails. Add the missed function to fix it. Fixes: de1135d44f4f ("[media] omap3isp: CCDC, preview engine and resizer") Signed-off-by: Chuhong Yuan Reviewed-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/isppreview.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c index 4dbdf3180d10..607b7685c982 100644 --- a/drivers/media/platform/omap3isp/isppreview.c +++ b/drivers/media/platform/omap3isp/isppreview.c @@ -2287,7 +2287,7 @@ static int preview_init_entities(struct isp_prev_device *prev) me->ops = &preview_media_ops; ret = media_entity_pads_init(me, PREV_PADS_NUM, pads); if (ret < 0) - return ret; + goto error_handler_free; preview_init_formats(sd, NULL); @@ -2320,6 +2320,8 @@ error_video_out: omap3isp_video_cleanup(&prev->video_in); error_video_in: media_entity_cleanup(&prev->subdev.entity); +error_handler_free: + v4l2_ctrl_handler_free(&prev->ctrls); return ret; } -- cgit v1.2.3 From d46cfdc86c30d5ec768924f0b1e2683c8d20b671 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Fri, 12 Jun 2020 15:53:46 +0200 Subject: media: i2c: imx290: set the format before VIDIOC_SUBDEV_G_FMT is called With the current driver 'media-ctl -p' issued right after the imx290 driver is loaded prints: pad0: Source [fmt:unknown/0x0] The format value of zero is due to the current_format field of the imx290 struct not being initialized yet. As imx290_entity_init_cfg() calls imx290_set_fmt(), the current_mode field is also initialized, so the line which set current_mode to a default value in driver's probe() function is no longer needed. Signed-off-by: Andrey Konovalov Reviewed-by: Manivannan Sadhasivam Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx290.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index f7678e5a5d87..2d8c38ffe2f0 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -722,9 +722,6 @@ static int imx290_probe(struct i2c_client *client) goto free_err; } - /* Set default mode to max resolution */ - imx290->current_mode = &imx290_modes[0]; - /* get system clock (xclk) */ imx290->xclk = devm_clk_get(dev, "xclk"); if (IS_ERR(imx290->xclk)) { @@ -809,6 +806,9 @@ static int imx290_probe(struct i2c_client *client) goto free_ctrl; } + /* Initialize the frame format (this also sets imx290->current_mode) */ + imx290_entity_init_cfg(&imx290->sd, NULL); + ret = v4l2_async_register_subdev(&imx290->sd); if (ret < 0) { dev_err(dev, "Could not register v4l2 device\n"); -- cgit v1.2.3 From 8d2d1bedb1b9af3e0c039a4444858da7b6da71f8 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Fri, 12 Jun 2020 15:53:47 +0200 Subject: media: i2c: imx290: fix the order of the args in SET_RUNTIME_PM_OPS() The macro is defined as SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn), so imx290_power_off must be the 1st arg, and imx290_power_on the 2nd. Signed-off-by: Andrey Konovalov Reviewed-by: Manivannan Sadhasivam Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx290.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 2d8c38ffe2f0..d0322f9a8856 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -648,7 +648,7 @@ static int imx290_power_off(struct device *dev) } static const struct dev_pm_ops imx290_pm_ops = { - SET_RUNTIME_PM_OPS(imx290_power_on, imx290_power_off, NULL) + SET_RUNTIME_PM_OPS(imx290_power_off, imx290_power_on, NULL) }; static const struct v4l2_subdev_video_ops imx290_video_ops = { -- cgit v1.2.3 From 3909a92d7df622b41b9ceeeea694e641cad7667b Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Fri, 12 Jun 2020 15:53:48 +0200 Subject: media: i2c: imx290: fix reset GPIO pin handling According to https://www.kernel.org/doc/Documentation/gpio/consumer.txt, - all of the gpiod_set_value_xxx() functions operate with the *logical* value. So in imx290_power_on() the reset signal should be cleared (de-asserted) with gpiod_set_value_cansleep(imx290->rst_gpio, 0), and in imx290_power_off() the value of 1 must be used to apply/assert the reset to the sensor. In the device tree the reset pin is described as GPIO_ACTIVE_LOW, and gpiod_set_value_xxx() functions take this into account, - when devm_gpiod_get_optional() is called with GPIOD_ASIS, the GPIO is not initialized, and the direction must be set later; using a GPIO without setting its direction first is illegal and will result in undefined behavior. Fix this by using GPIOD_OUT_HIGH instead of GPIOD_ASIS (this asserts the reset signal to the sensor initially). Signed-off-by: Andrey Konovalov Reviewed-by: Manivannan Sadhasivam Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx290.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index d0322f9a8856..7b1de1f0c8b7 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -628,7 +628,7 @@ static int imx290_power_on(struct device *dev) } usleep_range(1, 2); - gpiod_set_value_cansleep(imx290->rst_gpio, 1); + gpiod_set_value_cansleep(imx290->rst_gpio, 0); usleep_range(30000, 31000); return 0; @@ -641,7 +641,7 @@ static int imx290_power_off(struct device *dev) struct imx290 *imx290 = to_imx290(sd); clk_disable_unprepare(imx290->xclk); - gpiod_set_value_cansleep(imx290->rst_gpio, 0); + gpiod_set_value_cansleep(imx290->rst_gpio, 1); regulator_bulk_disable(IMX290_NUM_SUPPLIES, imx290->supplies); return 0; @@ -757,7 +757,8 @@ static int imx290_probe(struct i2c_client *client) goto free_err; } - imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS); + imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); if (IS_ERR(imx290->rst_gpio)) { dev_err(dev, "Cannot get reset gpio\n"); ret = PTR_ERR(imx290->rst_gpio); -- cgit v1.2.3 From 97589ad61c730e0f486635c6c19fa25ab8e8f29d Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 12 Jun 2020 15:53:49 +0200 Subject: media: i2c: imx290: Add support for 2 data lanes The IMX290 sensor can output frames with 2/4 CSI2 data lanes. This commit adds support for 2 lane mode in addition to the 4 lane and also configuring the data lane settings in the driver based on system configuration. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Andrey Konovalov Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx290.c | 147 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 133 insertions(+), 14 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 7b1de1f0c8b7..ffb393962ffc 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -25,7 +25,12 @@ #define IMX290_STANDBY 0x3000 #define IMX290_REGHOLD 0x3001 #define IMX290_XMSTA 0x3002 +#define IMX290_FR_FDG_SEL 0x3009 #define IMX290_GAIN 0x3014 +#define IMX290_HMAX_LOW 0x301c +#define IMX290_HMAX_HIGH 0x301d +#define IMX290_PHY_LANE_NUM 0x3407 +#define IMX290_CSI_LANE_MODE 0x3443 #define IMX290_DEFAULT_LINK_FREQ 445500000 @@ -45,6 +50,7 @@ struct imx290_regval { struct imx290_mode { u32 width; u32 height; + u32 hmax; u32 pixel_rate; u32 link_freq_index; @@ -56,6 +62,7 @@ struct imx290 { struct device *dev; struct clk *xclk; struct regmap *regmap; + u8 nlanes; struct v4l2_subdev sd; struct v4l2_fwnode_endpoint ep; @@ -89,14 +96,11 @@ static const struct regmap_config imx290_regmap_config = { static const struct imx290_regval imx290_global_init_settings[] = { { 0x3007, 0x00 }, - { 0x3009, 0x00 }, { 0x3018, 0x65 }, { 0x3019, 0x04 }, { 0x301a, 0x00 }, - { 0x3443, 0x03 }, { 0x3444, 0x20 }, { 0x3445, 0x25 }, - { 0x3407, 0x03 }, { 0x303a, 0x0c }, { 0x3040, 0x00 }, { 0x3041, 0x00 }, @@ -169,7 +173,6 @@ static const struct imx290_regval imx290_1080p_settings[] = { { 0x3164, 0x1a }, { 0x3480, 0x49 }, /* data rate settings */ - { 0x3009, 0x01 }, { 0x3405, 0x10 }, { 0x3446, 0x57 }, { 0x3447, 0x00 }, @@ -187,8 +190,6 @@ static const struct imx290_regval imx290_1080p_settings[] = { { 0x3453, 0x00 }, { 0x3454, 0x17 }, { 0x3455, 0x00 }, - { 0x301c, 0x98 }, - { 0x301d, 0x08 }, }; static const struct imx290_regval imx290_720p_settings[] = { @@ -210,7 +211,6 @@ static const struct imx290_regval imx290_720p_settings[] = { { 0x3164, 0x1a }, { 0x3480, 0x49 }, /* data rate settings */ - { 0x3009, 0x01 }, { 0x3405, 0x10 }, { 0x3446, 0x4f }, { 0x3447, 0x00 }, @@ -228,8 +228,6 @@ static const struct imx290_regval imx290_720p_settings[] = { { 0x3453, 0x00 }, { 0x3454, 0x17 }, { 0x3455, 0x00 }, - { 0x301c, 0xe4 }, - { 0x301d, 0x0c }, }; static const struct imx290_regval imx290_10bit_settings[] = { @@ -250,10 +248,11 @@ static const s64 imx290_link_freq[] = { }; /* Mode configs */ -static const struct imx290_mode imx290_modes[] = { +static const struct imx290_mode imx290_modes_2lanes[] = { { .width = 1920, .height = 1080, + .hmax = 0x1130, .data = imx290_1080p_settings, .data_size = ARRAY_SIZE(imx290_1080p_settings), .pixel_rate = 178200000, @@ -262,6 +261,7 @@ static const struct imx290_mode imx290_modes[] = { { .width = 1280, .height = 720, + .hmax = 0x19c8, .data = imx290_720p_settings, .data_size = ARRAY_SIZE(imx290_720p_settings), .pixel_rate = 178200000, @@ -269,6 +269,44 @@ static const struct imx290_mode imx290_modes[] = { }, }; +static const struct imx290_mode imx290_modes_4lanes[] = { + { + .width = 1920, + .height = 1080, + .hmax = 0x0898, + .data = imx290_1080p_settings, + .data_size = ARRAY_SIZE(imx290_1080p_settings), + .pixel_rate = 178200000, + .link_freq_index = 0, + }, + { + .width = 1280, + .height = 720, + .hmax = 0x0ce4, + .data = imx290_720p_settings, + .data_size = ARRAY_SIZE(imx290_720p_settings), + .pixel_rate = 178200000, + .link_freq_index = 0, + }, +}; + +static inline const struct imx290_mode *imx290_modes_ptr(const struct imx290 *imx290) +{ + /* We rely on imx290_probe() to ensure that nlanes is either 2 or 4 */ + if (imx290->nlanes == 2) + return imx290_modes_2lanes; + else + return imx290_modes_4lanes; +} + +static inline int imx290_modes_num(const struct imx290 *imx290) +{ + if (imx290->nlanes == 2) + return ARRAY_SIZE(imx290_modes_2lanes); + else + return ARRAY_SIZE(imx290_modes_4lanes); +} + static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd) { return container_of(_sd, struct imx290, sd); @@ -450,9 +488,8 @@ static int imx290_set_fmt(struct v4l2_subdev *sd, mutex_lock(&imx290->lock); - mode = v4l2_find_nearest_size(imx290_modes, - ARRAY_SIZE(imx290_modes), - width, height, + mode = v4l2_find_nearest_size(imx290_modes_ptr(imx290), + imx290_modes_num(imx290), width, height, fmt->format.width, fmt->format.height); fmt->format.width = mode->width; @@ -522,6 +559,25 @@ static int imx290_write_current_format(struct imx290 *imx290, return 0; } +static int imx290_set_hmax(struct imx290 *imx290, u32 val) +{ + int ret; + + ret = imx290_write_reg(imx290, IMX290_HMAX_LOW, (val & 0xff)); + if (ret) { + dev_err(imx290->dev, "Error setting HMAX register\n"); + return ret; + } + + ret = imx290_write_reg(imx290, IMX290_HMAX_HIGH, ((val >> 8) & 0xff)); + if (ret) { + dev_err(imx290->dev, "Error setting HMAX register\n"); + return ret; + } + + return 0; +} + /* Start streaming */ static int imx290_start_streaming(struct imx290 *imx290) { @@ -550,6 +606,9 @@ static int imx290_start_streaming(struct imx290 *imx290) dev_err(imx290->dev, "Could not set current mode\n"); return ret; } + ret = imx290_set_hmax(imx290, imx290->current_mode->hmax); + if (ret < 0) + return ret; /* Apply customized values from user */ ret = v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler); @@ -607,6 +666,49 @@ static int imx290_get_regulators(struct device *dev, struct imx290 *imx290) imx290->supplies); } +static int imx290_set_data_lanes(struct imx290 *imx290) +{ + int ret = 0, laneval, frsel; + + switch (imx290->nlanes) { + case 2: + laneval = 0x01; + frsel = 0x02; + break; + case 4: + laneval = 0x03; + frsel = 0x01; + break; + default: + /* + * We should never hit this since the data lane count is + * validated in probe itself + */ + dev_err(imx290->dev, "Lane configuration not supported\n"); + ret = -EINVAL; + goto exit; + } + + ret = imx290_write_reg(imx290, IMX290_PHY_LANE_NUM, laneval); + if (ret) { + dev_err(imx290->dev, "Error setting Physical Lane number register\n"); + goto exit; + } + + ret = imx290_write_reg(imx290, IMX290_CSI_LANE_MODE, laneval); + if (ret) { + dev_err(imx290->dev, "Error setting CSI Lane mode register\n"); + goto exit; + } + + ret = imx290_write_reg(imx290, IMX290_FR_FDG_SEL, frsel); + if (ret) + dev_err(imx290->dev, "Error setting FR/FDG SEL register\n"); + +exit: + return ret; +} + static int imx290_power_on(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -631,6 +733,9 @@ static int imx290_power_on(struct device *dev) gpiod_set_value_cansleep(imx290->rst_gpio, 0); usleep_range(30000, 31000); + /* Set data lane count */ + imx290_set_data_lanes(imx290); + return 0; } @@ -677,6 +782,7 @@ static int imx290_probe(struct i2c_client *client) struct fwnode_handle *endpoint; struct imx290 *imx290; u32 xclk_freq; + u32 default_pixel_rate; int ret; imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL); @@ -703,6 +809,16 @@ static int imx290_probe(struct i2c_client *client) goto free_err; } + /* Get number of data lanes */ + imx290->nlanes = imx290->ep.bus.mipi_csi2.num_data_lanes; + if (imx290->nlanes != 2 && imx290->nlanes != 4) { + dev_err(dev, "Invalid data lanes: %d\n", imx290->nlanes); + ret = -EINVAL; + goto free_err; + } + + dev_dbg(dev, "Using %u data lanes\n", imx290->nlanes); + if (!imx290->ep.nr_of_link_frequencies) { dev_err(dev, "link-frequency property not found in DT\n"); ret = -EINVAL; @@ -780,10 +896,13 @@ static int imx290_probe(struct i2c_client *client) if (imx290->link_freq) imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + default_pixel_rate = imx290->nlanes == 2 ? + imx290_modes_2lanes[0].pixel_rate : + imx290_modes_4lanes[0].pixel_rate; imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, - imx290_modes[0].pixel_rate); + default_pixel_rate); imx290->sd.ctrl_handler = &imx290->ctrls; -- cgit v1.2.3 From 98e0500eadb772e1be32d8e369fcc3b7bcac93ed Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 12 Jun 2020 15:53:50 +0200 Subject: media: i2c: imx290: Add configurable link frequency and pixel rate IMX290 operates with multiple link frequency and pixel rate combinations. The initial driver used a single setting for both but since we now have the lane count support in place, let's add configurable link frequency and pixel rate. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Andrey Konovalov Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx290.c | 148 +++++++++++++++++++++++++++++++++------------ 1 file changed, 109 insertions(+), 39 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index ffb393962ffc..152b65cb7cbc 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -32,8 +32,6 @@ #define IMX290_PHY_LANE_NUM 0x3407 #define IMX290_CSI_LANE_MODE 0x3443 -#define IMX290_DEFAULT_LINK_FREQ 445500000 - static const char * const imx290_supply_name[] = { "vdda", "vddd", @@ -51,8 +49,7 @@ struct imx290_mode { u32 width; u32 height; u32 hmax; - u32 pixel_rate; - u32 link_freq_index; + u8 link_freq_index; const struct imx290_regval *data; u32 data_size; @@ -243,29 +240,54 @@ static const struct imx290_regval imx290_10bit_settings[] = { }; /* supported link frequencies */ -static const s64 imx290_link_freq[] = { - IMX290_DEFAULT_LINK_FREQ, +#define FREQ_INDEX_1080P 0 +#define FREQ_INDEX_720P 1 +static const s64 imx290_link_freq_2lanes[] = { + [FREQ_INDEX_1080P] = 445500000, + [FREQ_INDEX_720P] = 297000000, +}; +static const s64 imx290_link_freq_4lanes[] = { + [FREQ_INDEX_1080P] = 222750000, + [FREQ_INDEX_720P] = 148500000, }; +/* + * In this function and in the similar ones below We rely on imx290_probe() + * to ensure that nlanes is either 2 or 4. + */ +static inline const s64 *imx290_link_freqs_ptr(const struct imx290 *imx290) +{ + if (imx290->nlanes == 2) + return imx290_link_freq_2lanes; + else + return imx290_link_freq_4lanes; +} + +static inline int imx290_link_freqs_num(const struct imx290 *imx290) +{ + if (imx290->nlanes == 2) + return ARRAY_SIZE(imx290_link_freq_2lanes); + else + return ARRAY_SIZE(imx290_link_freq_4lanes); +} + /* Mode configs */ static const struct imx290_mode imx290_modes_2lanes[] = { { .width = 1920, .height = 1080, .hmax = 0x1130, + .link_freq_index = FREQ_INDEX_1080P, .data = imx290_1080p_settings, .data_size = ARRAY_SIZE(imx290_1080p_settings), - .pixel_rate = 178200000, - .link_freq_index = 0, }, { .width = 1280, .height = 720, .hmax = 0x19c8, + .link_freq_index = FREQ_INDEX_720P, .data = imx290_720p_settings, .data_size = ARRAY_SIZE(imx290_720p_settings), - .pixel_rate = 178200000, - .link_freq_index = 0, }, }; @@ -274,25 +296,22 @@ static const struct imx290_mode imx290_modes_4lanes[] = { .width = 1920, .height = 1080, .hmax = 0x0898, + .link_freq_index = FREQ_INDEX_1080P, .data = imx290_1080p_settings, .data_size = ARRAY_SIZE(imx290_1080p_settings), - .pixel_rate = 178200000, - .link_freq_index = 0, }, { .width = 1280, .height = 720, .hmax = 0x0ce4, + .link_freq_index = FREQ_INDEX_720P, .data = imx290_720p_settings, .data_size = ARRAY_SIZE(imx290_720p_settings), - .pixel_rate = 178200000, - .link_freq_index = 0, }, }; static inline const struct imx290_mode *imx290_modes_ptr(const struct imx290 *imx290) { - /* We rely on imx290_probe() to ensure that nlanes is either 2 or 4 */ if (imx290->nlanes == 2) return imx290_modes_2lanes; else @@ -477,6 +496,30 @@ static int imx290_get_fmt(struct v4l2_subdev *sd, return 0; } +static inline u8 imx290_get_link_freq_index(struct imx290 *imx290) +{ + return imx290->current_mode->link_freq_index; +} + +static s64 imx290_get_link_freq(struct imx290 *imx290) +{ + u8 index = imx290_get_link_freq_index(imx290); + + return *(imx290_link_freqs_ptr(imx290) + index); +} + +static u64 imx290_calc_pixel_rate(struct imx290 *imx290) +{ + s64 link_freq = imx290_get_link_freq(imx290); + u8 nlanes = imx290->nlanes; + u64 pixel_rate; + + /* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ + pixel_rate = link_freq * 2 * nlanes; + do_div(pixel_rate, 10); + return pixel_rate; +} + static int imx290_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) @@ -509,10 +552,14 @@ static int imx290_set_fmt(struct v4l2_subdev *sd, format = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); } else { format = &imx290->current_format; - __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index); - __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, mode->pixel_rate); - imx290->current_mode = mode; + + if (imx290->link_freq) + __v4l2_ctrl_s_ctrl(imx290->link_freq, + imx290_get_link_freq_index(imx290)); + if (imx290->pixel_rate) + __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, + imx290_calc_pixel_rate(imx290)); } *format = fmt->format; @@ -536,12 +583,11 @@ static int imx290_entity_init_cfg(struct v4l2_subdev *subdev, return 0; } -static int imx290_write_current_format(struct imx290 *imx290, - struct v4l2_mbus_framefmt *format) +static int imx290_write_current_format(struct imx290 *imx290) { int ret; - switch (format->code) { + switch (imx290->current_format.code) { case MEDIA_BUS_FMT_SRGGB10_1X10: ret = imx290_set_register_array(imx290, imx290_10bit_settings, ARRAY_SIZE( @@ -592,8 +638,8 @@ static int imx290_start_streaming(struct imx290 *imx290) return ret; } - /* Set current frame format */ - ret = imx290_write_current_format(imx290, &imx290->current_format); + /* Apply the register values related to current frame format */ + ret = imx290_write_current_format(imx290); if (ret < 0) { dev_err(imx290->dev, "Could not set frame format\n"); return ret; @@ -776,13 +822,34 @@ static const struct media_entity_operations imx290_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; +/* + * Returns 0 if all link frequencies used by the driver for the given number + * of MIPI data lanes are mentioned in the device tree, or the value of the + * first missing frequency otherwise. + */ +static s64 imx290_check_link_freqs(const struct imx290 *imx290) +{ + int i, j; + const s64 *freqs = imx290_link_freqs_ptr(imx290); + int freqs_count = imx290_link_freqs_num(imx290); + + for (i = 0; i < freqs_count; i++) { + for (j = 0; j < imx290->ep.nr_of_link_frequencies; j++) + if (freqs[i] == imx290->ep.link_frequencies[j]) + break; + if (j == imx290->ep.nr_of_link_frequencies) + return freqs[i]; + } + return 0; +} + static int imx290_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct fwnode_handle *endpoint; struct imx290 *imx290; u32 xclk_freq; - u32 default_pixel_rate; + s64 fq; int ret; imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL); @@ -825,8 +892,10 @@ static int imx290_probe(struct i2c_client *client) goto free_err; } - if (imx290->ep.link_frequencies[0] != IMX290_DEFAULT_LINK_FREQ) { - dev_err(dev, "Unsupported link frequency\n"); + /* Check that link frequences for all the modes are in device tree */ + fq = imx290_check_link_freqs(imx290); + if (fq) { + dev_err(dev, "Link frequency of %lld is not supported\n", fq); ret = -EINVAL; goto free_err; } @@ -883,26 +952,30 @@ static int imx290_probe(struct i2c_client *client) mutex_init(&imx290->lock); + /* + * Initialize the frame format. In particular, imx290->current_mode + * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() call + * below relies on these fields. + */ + imx290_entity_init_cfg(&imx290->sd, NULL); + v4l2_ctrl_handler_init(&imx290->ctrls, 3); v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_GAIN, 0, 72, 1, 0); + imx290->link_freq = - v4l2_ctrl_new_int_menu(&imx290->ctrls, - &imx290_ctrl_ops, + v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_LINK_FREQ, - ARRAY_SIZE(imx290_link_freq) - 1, - 0, imx290_link_freq); + imx290_link_freqs_num(imx290) - 1, 0, + imx290_link_freqs_ptr(imx290)); if (imx290->link_freq) imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; - default_pixel_rate = imx290->nlanes == 2 ? - imx290_modes_2lanes[0].pixel_rate : - imx290_modes_4lanes[0].pixel_rate; imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, - V4L2_CID_PIXEL_RATE, 1, - INT_MAX, 1, - default_pixel_rate); + V4L2_CID_PIXEL_RATE, + 1, INT_MAX, 1, + imx290_calc_pixel_rate(imx290)); imx290->sd.ctrl_handler = &imx290->ctrls; @@ -926,9 +999,6 @@ static int imx290_probe(struct i2c_client *client) goto free_ctrl; } - /* Initialize the frame format (this also sets imx290->current_mode) */ - imx290_entity_init_cfg(&imx290->sd, NULL); - ret = v4l2_async_register_subdev(&imx290->sd); if (ret < 0) { dev_err(dev, "Could not register v4l2 device\n"); -- cgit v1.2.3 From a58df1f9e4885eaf3d0663574a217e513821a9f0 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 12 Jun 2020 15:53:51 +0200 Subject: media: i2c: imx290: Add support for test pattern generation Add support for generating following test patterns by IMX290: * Sequence Pattern 1 * Horizontal Color-bar Chart * Vertical Color-bar Chart * Sequence Pattern 2 * Gradation Pattern 1 * Gradation Pattern 2 * 000/555h Toggle Pattern Signed-off-by: Manivannan Sadhasivam Signed-off-by: Andrey Konovalov Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx290.c | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 152b65cb7cbc..67725a5aabd3 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -26,12 +26,19 @@ #define IMX290_REGHOLD 0x3001 #define IMX290_XMSTA 0x3002 #define IMX290_FR_FDG_SEL 0x3009 +#define IMX290_BLKLEVEL_LOW 0x300a +#define IMX290_BLKLEVEL_HIGH 0x300b #define IMX290_GAIN 0x3014 #define IMX290_HMAX_LOW 0x301c #define IMX290_HMAX_HIGH 0x301d +#define IMX290_PGCTRL 0x308c #define IMX290_PHY_LANE_NUM 0x3407 #define IMX290_CSI_LANE_MODE 0x3443 +#define IMX290_PGCTRL_REGEN BIT(0) +#define IMX290_PGCTRL_THRU BIT(1) +#define IMX290_PGCTRL_MODE(n) ((n) << 4) + static const char * const imx290_supply_name[] = { "vdda", "vddd", @@ -91,6 +98,17 @@ static const struct regmap_config imx290_regmap_config = { .cache_type = REGCACHE_RBTREE, }; +static const char * const imx290_test_pattern_menu[] = { + "Disabled", + "Sequence Pattern 1", + "Horizontal Color-bar Chart", + "Vertical Color-bar Chart", + "Sequence Pattern 2", + "Gradation Pattern 1", + "Gradation Pattern 2", + "000/555h Toggle Pattern", +}; + static const struct imx290_regval imx290_global_init_settings[] = { { 0x3007, 0x00 }, { 0x3018, 0x65 }, @@ -448,6 +466,22 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_GAIN: ret = imx290_set_gain(imx290, ctrl->val); break; + case V4L2_CID_TEST_PATTERN: + if (ctrl->val) { + imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, 0x00); + imx290_write_reg(imx290, IMX290_BLKLEVEL_HIGH, 0x00); + msleep(10); + imx290_write_reg(imx290, IMX290_PGCTRL, + (u8)(IMX290_PGCTRL_REGEN | + IMX290_PGCTRL_THRU | + IMX290_PGCTRL_MODE(ctrl->val))); + } else { + imx290_write_reg(imx290, IMX290_PGCTRL, 0x00); + msleep(10); + imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, 0x3c); + imx290_write_reg(imx290, IMX290_BLKLEVEL_HIGH, 0x00); + } + break; default: ret = -EINVAL; break; @@ -959,7 +993,7 @@ static int imx290_probe(struct i2c_client *client) */ imx290_entity_init_cfg(&imx290->sd, NULL); - v4l2_ctrl_handler_init(&imx290->ctrls, 3); + v4l2_ctrl_handler_init(&imx290->ctrls, 4); v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_GAIN, 0, 72, 1, 0); @@ -977,6 +1011,11 @@ static int imx290_probe(struct i2c_client *client) 1, INT_MAX, 1, imx290_calc_pixel_rate(imx290)); + v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx290_test_pattern_menu) - 1, + 0, 0, imx290_test_pattern_menu); + imx290->sd.ctrl_handler = &imx290->ctrls; if (imx290->ctrls.error) { -- cgit v1.2.3 From c566ac01ceaa02450acc155201772c0623530e76 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 12 Jun 2020 15:53:52 +0200 Subject: media: i2c: imx290: Add RAW12 mode support IMX290 is capable of outputting frames in both Raw Bayer (packed) 10 and 12 bit formats. Since the driver already supports RAW10 mode, let's add the missing RAW12 mode as well. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Andrey Konovalov Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx290.c | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 67725a5aabd3..c654a9a8fb08 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -67,6 +67,7 @@ struct imx290 { struct clk *xclk; struct regmap *regmap; u8 nlanes; + u8 bpp; struct v4l2_subdev sd; struct v4l2_fwnode_endpoint ep; @@ -86,10 +87,12 @@ struct imx290 { struct imx290_pixfmt { u32 code; + u8 bpp; }; static const struct imx290_pixfmt imx290_formats[] = { - { MEDIA_BUS_FMT_SRGGB10_1X10 }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, }; static const struct regmap_config imx290_regmap_config = { @@ -257,6 +260,18 @@ static const struct imx290_regval imx290_10bit_settings[] = { { 0x300b, 0x00}, }; +static const struct imx290_regval imx290_12bit_settings[] = { + { 0x3005, 0x01 }, + { 0x3046, 0x01 }, + { 0x3129, 0x00 }, + { 0x317c, 0x00 }, + { 0x31ec, 0x0e }, + { 0x3441, 0x0c }, + { 0x3442, 0x0c }, + { 0x300a, 0xf0 }, + { 0x300b, 0x00 }, +}; + /* supported link frequencies */ #define FREQ_INDEX_1080P 0 #define FREQ_INDEX_720P 1 @@ -478,7 +493,12 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) } else { imx290_write_reg(imx290, IMX290_PGCTRL, 0x00); msleep(10); - imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, 0x3c); + if (imx290->bpp == 10) + imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, + 0x3c); + else /* 12 bits per pixel */ + imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, + 0xf0); imx290_write_reg(imx290, IMX290_BLKLEVEL_HIGH, 0x00); } break; @@ -550,7 +570,7 @@ static u64 imx290_calc_pixel_rate(struct imx290 *imx290) /* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ pixel_rate = link_freq * 2 * nlanes; - do_div(pixel_rate, 10); + do_div(pixel_rate, imx290->bpp); return pixel_rate; } @@ -587,6 +607,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd, } else { format = &imx290->current_format; imx290->current_mode = mode; + imx290->bpp = imx290_formats[i].bpp; if (imx290->link_freq) __v4l2_ctrl_s_ctrl(imx290->link_freq, @@ -631,6 +652,15 @@ static int imx290_write_current_format(struct imx290 *imx290) return ret; } break; + case MEDIA_BUS_FMT_SRGGB12_1X12: + ret = imx290_set_register_array(imx290, imx290_12bit_settings, + ARRAY_SIZE( + imx290_12bit_settings)); + if (ret < 0) { + dev_err(imx290->dev, "Could not set format registers\n"); + return ret; + } + break; default: dev_err(imx290->dev, "Unknown pixel format\n"); return -EINVAL; -- cgit v1.2.3 From 3b867fb641d884b714fba390ae866714ba475f29 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 12 Jun 2020 15:53:53 +0200 Subject: media: i2c: imx290: Add support to enumerate all frame sizes Add support to enumerate all frame sizes supported by IMX290. This is required for using with userspace tools such as libcamera. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Andrey Konovalov Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx290.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index c654a9a8fb08..fd147fac5ef2 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -528,6 +528,28 @@ static int imx290_enum_mbus_code(struct v4l2_subdev *sd, return 0; } +static int imx290_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + const struct imx290 *imx290 = to_imx290(sd); + const struct imx290_mode *imx290_modes = imx290_modes_ptr(imx290); + + if ((fse->code != imx290_formats[0].code) && + (fse->code != imx290_formats[1].code)) + return -EINVAL; + + if (fse->index >= imx290_modes_num(imx290)) + return -EINVAL; + + fse->min_width = imx290_modes[fse->index].width; + fse->max_width = imx290_modes[fse->index].width; + fse->min_height = imx290_modes[fse->index].height; + fse->max_height = imx290_modes[fse->index].height; + + return 0; +} + static int imx290_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) @@ -873,6 +895,7 @@ static const struct v4l2_subdev_video_ops imx290_video_ops = { static const struct v4l2_subdev_pad_ops imx290_pad_ops = { .init_cfg = imx290_entity_init_cfg, .enum_mbus_code = imx290_enum_mbus_code, + .enum_frame_size = imx290_enum_frame_size, .get_fmt = imx290_get_fmt, .set_fmt = imx290_set_fmt, }; -- cgit v1.2.3 From 6544af9b04b4484867c234ba0be1b5008e4a14ee Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 12 Jun 2020 15:53:54 +0200 Subject: media: i2c: imx290: Move the settle time delay out of loop The 10ms settle time is needed only at the end of all consecutive register writes. So move the delay to outside of the for loop of imx290_set_register_array(). Signed-off-by: Manivannan Sadhasivam Signed-off-by: Andrey Konovalov Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx290.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index fd147fac5ef2..02001c1b0dfc 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -404,11 +404,11 @@ static int imx290_set_register_array(struct imx290 *imx290, ret = imx290_write_reg(imx290, settings->reg, settings->val); if (ret < 0) return ret; - - /* Settle time is 10ms for all registers */ - msleep(10); } + /* Provide 10ms settle time */ + msleep(10); + return 0; } -- cgit v1.2.3 From a270675875829b6d46eb9e38960fd6019555ebb8 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Fri, 12 Jun 2020 15:53:55 +0200 Subject: media: i2c: imx290: set bus_type before calling v4l2_fwnode_endpoint_alloc_parse() The bus_type field of v4l2_fwnode_endpoint structure passed as the argument to v4l2_fwnode_endpoint_alloc_parse() function must be initiaized. Set it to V4L2_MBUS_CSI2_DPHY, and check for -ENXIO which is returned when the requested media bus type doesn't match the fwnode. Also remove v4l2_fwnode_endpoint field from struct imx290 as it is only needed in the probe function: use the local variable for this purpose. Signed-off-by: Andrey Konovalov Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx290.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 02001c1b0dfc..9c97830164e9 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -70,7 +70,6 @@ struct imx290 { u8 bpp; struct v4l2_subdev sd; - struct v4l2_fwnode_endpoint ep; struct media_pad pad; struct v4l2_mbus_framefmt current_format; const struct imx290_mode *current_mode; @@ -914,17 +913,18 @@ static const struct media_entity_operations imx290_subdev_entity_ops = { * of MIPI data lanes are mentioned in the device tree, or the value of the * first missing frequency otherwise. */ -static s64 imx290_check_link_freqs(const struct imx290 *imx290) +static s64 imx290_check_link_freqs(const struct imx290 *imx290, + const struct v4l2_fwnode_endpoint *ep) { int i, j; const s64 *freqs = imx290_link_freqs_ptr(imx290); int freqs_count = imx290_link_freqs_num(imx290); for (i = 0; i < freqs_count; i++) { - for (j = 0; j < imx290->ep.nr_of_link_frequencies; j++) - if (freqs[i] == imx290->ep.link_frequencies[j]) + for (j = 0; j < ep->nr_of_link_frequencies; j++) + if (freqs[i] == ep->link_frequencies[j]) break; - if (j == imx290->ep.nr_of_link_frequencies) + if (j == ep->nr_of_link_frequencies) return freqs[i]; } return 0; @@ -934,6 +934,10 @@ static int imx290_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct fwnode_handle *endpoint; + /* Only CSI2 is supported for now: */ + struct v4l2_fwnode_endpoint ep = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; struct imx290 *imx290; u32 xclk_freq; s64 fq; @@ -956,15 +960,18 @@ static int imx290_probe(struct i2c_client *client) return -EINVAL; } - ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &imx290->ep); + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep); fwnode_handle_put(endpoint); - if (ret) { + if (ret == -ENXIO) { + dev_err(dev, "Unsupported bus type, should be CSI2\n"); + goto free_err; + } else if (ret) { dev_err(dev, "Parsing endpoint node failed\n"); goto free_err; } /* Get number of data lanes */ - imx290->nlanes = imx290->ep.bus.mipi_csi2.num_data_lanes; + imx290->nlanes = ep.bus.mipi_csi2.num_data_lanes; if (imx290->nlanes != 2 && imx290->nlanes != 4) { dev_err(dev, "Invalid data lanes: %d\n", imx290->nlanes); ret = -EINVAL; @@ -973,27 +980,20 @@ static int imx290_probe(struct i2c_client *client) dev_dbg(dev, "Using %u data lanes\n", imx290->nlanes); - if (!imx290->ep.nr_of_link_frequencies) { + if (!ep.nr_of_link_frequencies) { dev_err(dev, "link-frequency property not found in DT\n"); ret = -EINVAL; goto free_err; } /* Check that link frequences for all the modes are in device tree */ - fq = imx290_check_link_freqs(imx290); + fq = imx290_check_link_freqs(imx290, &ep); if (fq) { dev_err(dev, "Link frequency of %lld is not supported\n", fq); ret = -EINVAL; goto free_err; } - /* Only CSI2 is supported for now */ - if (imx290->ep.bus_type != V4L2_MBUS_CSI2_DPHY) { - dev_err(dev, "Unsupported bus type, should be CSI2\n"); - ret = -EINVAL; - goto free_err; - } - /* get system clock (xclk) */ imx290->xclk = devm_clk_get(dev, "xclk"); if (IS_ERR(imx290->xclk)) { @@ -1108,7 +1108,7 @@ static int imx290_probe(struct i2c_client *client) pm_runtime_enable(dev); pm_runtime_idle(dev); - v4l2_fwnode_endpoint_free(&imx290->ep); + v4l2_fwnode_endpoint_free(&ep); return 0; @@ -1118,7 +1118,7 @@ free_ctrl: v4l2_ctrl_handler_free(&imx290->ctrls); mutex_destroy(&imx290->lock); free_err: - v4l2_fwnode_endpoint_free(&imx290->ep); + v4l2_fwnode_endpoint_free(&ep); return ret; } -- cgit v1.2.3 From 7b981288285f0e8b816ea7ba8f34d0973ee39e0d Mon Sep 17 00:00:00 2001 From: Qingwu Zhang Date: Fri, 12 Jun 2020 11:42:02 +0200 Subject: media: ov2740: add NVMEM interface to read customized OTP data ov2740 includes 512bytes of one-time programmable memory and 256 bytes are reserved for customers which can be used to store customized information. This patch provide an NVMEM interface to support read out the customized data in OTP. Signed-off-by: Bingbu Cao Signed-off-by: Qingwu Zhang Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov2740.c | 145 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index 33025c6d23ac..fd0b6a903ec1 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include #include @@ -59,6 +61,21 @@ #define OV2740_TEST_PATTERN_ENABLE BIT(7) #define OV2740_TEST_PATTERN_BAR_SHIFT 2 +/* ISP CTRL00 */ +#define OV2740_REG_ISP_CTRL00 0x5000 +/* ISP CTRL01 */ +#define OV2740_REG_ISP_CTRL01 0x5001 +/* Customer Addresses: 0x7010 - 0x710F */ +#define CUSTOMER_USE_OTP_SIZE 0x100 +/* OTP registers from sensor */ +#define OV2740_REG_OTP_CUSTOMER 0x7010 + +struct nvm_data { + char *nvm_buffer; + struct nvmem_device *nvmem; + struct regmap *regmap; +}; + enum { OV2740_LINK_FREQ_360MHZ_INDEX, }; @@ -915,6 +932,130 @@ static int ov2740_remove(struct i2c_client *client) return 0; } +static int ov2740_load_otp_data(struct i2c_client *client, struct nvm_data *nvm) +{ + struct ov2740 *ov2740 = to_ov2740(i2c_get_clientdata(client)); + u32 isp_ctrl00 = 0; + u32 isp_ctrl01 = 0; + int ret; + + ret = ov2740_read_reg(ov2740, OV2740_REG_ISP_CTRL00, 1, &isp_ctrl00); + if (ret) { + dev_err(&client->dev, "failed to read ISP CTRL00\n"); + goto exit; + } + ret = ov2740_read_reg(ov2740, OV2740_REG_ISP_CTRL01, 1, &isp_ctrl01); + if (ret) { + dev_err(&client->dev, "failed to read ISP CTRL01\n"); + goto exit; + } + + /* Clear bit 5 of ISP CTRL00 */ + ret = ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL00, 1, + isp_ctrl00 & ~BIT(5)); + if (ret) { + dev_err(&client->dev, "failed to write ISP CTRL00\n"); + goto exit; + } + + /* Clear bit 7 of ISP CTRL01 */ + ret = ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL01, 1, + isp_ctrl01 & ~BIT(7)); + if (ret) { + dev_err(&client->dev, "failed to write ISP CTRL01\n"); + goto exit; + } + + ret = ov2740_write_reg(ov2740, OV2740_REG_MODE_SELECT, 1, + OV2740_MODE_STREAMING); + if (ret) { + dev_err(&client->dev, "failed to start streaming\n"); + goto exit; + } + + /* + * Users are not allowed to access OTP-related registers and memory + * during the 20 ms period after streaming starts (0x100 = 0x01). + */ + msleep(20); + + ret = regmap_bulk_read(nvm->regmap, OV2740_REG_OTP_CUSTOMER, + nvm->nvm_buffer, CUSTOMER_USE_OTP_SIZE); + if (ret) { + dev_err(&client->dev, "failed to read OTP data, ret %d\n", ret); + goto exit; + } + + ov2740_write_reg(ov2740, OV2740_REG_MODE_SELECT, 1, + OV2740_MODE_STANDBY); + ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL01, 1, isp_ctrl01); + ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL00, 1, isp_ctrl00); + +exit: + return ret; +} + +static int ov2740_nvmem_read(void *priv, unsigned int off, void *val, + size_t count) +{ + struct nvm_data *nvm = priv; + + memcpy(val, nvm->nvm_buffer + off, count); + + return 0; +} + +static int ov2740_register_nvmem(struct i2c_client *client) +{ + struct nvm_data *nvm; + struct regmap_config regmap_config = { }; + struct nvmem_config nvmem_config = { }; + struct regmap *regmap; + struct device *dev = &client->dev; + int ret = 0; + + nvm = devm_kzalloc(dev, sizeof(*nvm), GFP_KERNEL); + if (!nvm) + return -ENOMEM; + + regmap_config.val_bits = 8; + regmap_config.reg_bits = 16; + regmap_config.disable_locking = true; + regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + nvm->regmap = regmap; + + nvmem_config.name = dev_name(dev); + nvmem_config.dev = dev; + nvmem_config.read_only = true; + nvmem_config.root_only = true; + nvmem_config.owner = THIS_MODULE; + nvmem_config.compat = true; + nvmem_config.base_dev = dev; + nvmem_config.reg_read = ov2740_nvmem_read; + nvmem_config.reg_write = NULL; + nvmem_config.priv = nvm; + nvmem_config.stride = 1; + nvmem_config.word_size = 1; + nvmem_config.size = CUSTOMER_USE_OTP_SIZE; + + nvm->nvmem = devm_nvmem_register(dev, &nvmem_config); + if (IS_ERR(nvm->nvmem)) + return PTR_ERR(nvm->nvmem); + + nvm->nvm_buffer = devm_kzalloc(dev, CUSTOMER_USE_OTP_SIZE, GFP_KERNEL); + if (!nvm->nvm_buffer) + return -ENOMEM; + + ret = ov2740_load_otp_data(client, nvm); + if (ret) + dev_err(dev, "failed to load OTP data, ret %d\n", ret); + + return ret; +} + static int ov2740_probe(struct i2c_client *client) { struct ov2740 *ov2740; @@ -964,6 +1105,10 @@ static int ov2740_probe(struct i2c_client *client) goto probe_error_media_entity_cleanup; } + ret = ov2740_register_nvmem(client); + if (ret) + dev_err(&client->dev, "register nvmem failed, ret %d\n", ret); + /* * Device is already turned on by i2c-core with ACPI domain PM. * Enable runtime PM and turn off the device. -- cgit v1.2.3 From e30cc79cc80fd919b697a15c5000d9f57487de8e Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Sun, 21 Jun 2020 13:30:40 +0200 Subject: media: media-request: Fix crash if memory allocation fails Syzbot reports a NULL-ptr deref in the kref_put() call: BUG: KASAN: null-ptr-deref in media_request_put drivers/media/mc/mc-request.c:81 [inline] kref_put include/linux/kref.h:64 [inline] media_request_put drivers/media/mc/mc-request.c:81 [inline] media_request_close+0x4d/0x170 drivers/media/mc/mc-request.c:89 __fput+0x2ed/0x750 fs/file_table.c:281 task_work_run+0x147/0x1d0 kernel/task_work.c:123 tracehook_notify_resume include/linux/tracehook.h:188 [inline] exit_to_usermode_loop arch/x86/entry/common.c:165 [inline] prepare_exit_to_usermode+0x48e/0x600 arch/x86/entry/common.c:196 What led to this crash was an injected memory allocation failure in media_request_alloc(): FAULT_INJECTION: forcing a failure. name failslab, interval 1, probability 0, space 0, times 0 should_failslab+0x5/0x20 kmem_cache_alloc_trace+0x57/0x300 ? anon_inode_getfile+0xe5/0x170 media_request_alloc+0x339/0x440 media_device_request_alloc+0x94/0xc0 media_device_ioctl+0x1fb/0x330 ? do_vfs_ioctl+0x6ea/0x1a00 ? media_ioctl+0x101/0x120 ? __media_device_usb_init+0x430/0x430 ? media_poll+0x110/0x110 __se_sys_ioctl+0xf9/0x160 do_syscall_64+0xf3/0x1b0 When that allocation fails, filp->private_data is left uninitialized which media_request_close() does not expect and crashes. To avoid this, reorder media_request_alloc() such that allocating the struct file happens as the last step thus media_request_close() will no longer get called for a partially created media request. Reported-by: syzbot+6bed2d543cf7e48b822b@syzkaller.appspotmail.com Cc: stable@vger.kernel.org Signed-off-by: Tuomas Tynkkynen Fixes: 10905d70d788 ("media: media-request: implement media requests") Reviewed-by: Hans Verkuil Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/mc/mc-request.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/mc/mc-request.c b/drivers/media/mc/mc-request.c index e3fca436c75b..c0782fd96c59 100644 --- a/drivers/media/mc/mc-request.c +++ b/drivers/media/mc/mc-request.c @@ -296,9 +296,18 @@ int media_request_alloc(struct media_device *mdev, int *alloc_fd) if (WARN_ON(!mdev->ops->req_alloc ^ !mdev->ops->req_free)) return -ENOMEM; + if (mdev->ops->req_alloc) + req = mdev->ops->req_alloc(mdev); + else + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + fd = get_unused_fd_flags(O_CLOEXEC); - if (fd < 0) - return fd; + if (fd < 0) { + ret = fd; + goto err_free_req; + } filp = anon_inode_getfile("request", &request_fops, NULL, O_CLOEXEC); if (IS_ERR(filp)) { @@ -306,15 +315,6 @@ int media_request_alloc(struct media_device *mdev, int *alloc_fd) goto err_put_fd; } - if (mdev->ops->req_alloc) - req = mdev->ops->req_alloc(mdev); - else - req = kzalloc(sizeof(*req), GFP_KERNEL); - if (!req) { - ret = -ENOMEM; - goto err_fput; - } - filp->private_data = req; req->mdev = mdev; req->state = MEDIA_REQUEST_STATE_IDLE; @@ -336,12 +336,15 @@ int media_request_alloc(struct media_device *mdev, int *alloc_fd) return 0; -err_fput: - fput(filp); - err_put_fd: put_unused_fd(fd); +err_free_req: + if (mdev->ops->req_free) + mdev->ops->req_free(req); + else + kfree(req); + return ret; } -- cgit v1.2.3 From 50795910160e78c1bee6b27d8d7a3f93e8b7d48d Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Thu, 9 Apr 2020 13:24:04 +0200 Subject: media: camss: use proper media entity function for subdevices Currently for msm_csiphy, msm_csid, and msm_ispif subdevices the media entity function field is set to MEDIA_ENT_F_IO_V4L. This is incorrect as MEDIA_ENT_F_IO_V4L implies V4L2 video node. Change it to MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER as this is the best fit from the functions defined in include/uapi/linux/media.h. Signed-off-by: Andrey Konovalov Tested-by: Robert Foss Reviewed-by: Robert Foss Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-csid.c | 2 +- drivers/media/platform/qcom/camss/camss-csiphy.c | 2 +- drivers/media/platform/qcom/camss/camss-ispif.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index 8a247b6f5550..2ffcda06706b 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -1358,7 +1358,7 @@ int msm_csid_register_entity(struct csid_device *csid, pads[MSM_CSID_PAD_SINK].flags = MEDIA_PAD_FL_SINK; pads[MSM_CSID_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; - sd->entity.function = MEDIA_ENT_F_IO_V4L; + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; sd->entity.ops = &csid_media_ops; ret = media_entity_pads_init(&sd->entity, MSM_CSID_PADS_NUM, pads); if (ret < 0) { diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 008afb85023b..03ef9c5f4774 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -737,7 +737,7 @@ int msm_csiphy_register_entity(struct csiphy_device *csiphy, pads[MSM_CSIPHY_PAD_SINK].flags = MEDIA_PAD_FL_SINK; pads[MSM_CSIPHY_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; - sd->entity.function = MEDIA_ENT_F_IO_V4L; + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; sd->entity.ops = &csiphy_media_ops; ret = media_entity_pads_init(&sd->entity, MSM_CSIPHY_PADS_NUM, pads); if (ret < 0) { diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index 5722e971b184..db94cfd6c508 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -1325,7 +1325,7 @@ int msm_ispif_register_entities(struct ispif_device *ispif, pads[MSM_ISPIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK; pads[MSM_ISPIF_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; - sd->entity.function = MEDIA_ENT_F_IO_V4L; + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; sd->entity.ops = &ispif_media_ops; ret = media_entity_pads_init(&sd->entity, MSM_ISPIF_PADS_NUM, pads); -- cgit v1.2.3 From 7cd08d6eed2772e5ac759063a0287c5ff4551a91 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 19 Mar 2020 23:23:52 +0100 Subject: media: usb: pwc: pwc.h: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/pwc/pwc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/pwc/pwc.h b/drivers/media/usb/pwc/pwc.h index 3362962d0d00..b02a3c7b7742 100644 --- a/drivers/media/usb/pwc/pwc.h +++ b/drivers/media/usb/pwc/pwc.h @@ -193,7 +193,7 @@ struct pwc_raw_frame { decompressor) */ __u8 cmd[4]; /* the four byte of the command (in case of nala, only the first 3 bytes is filled) */ - __u8 rawframe[0]; /* frame_size = H / 4 * vbandlength */ + __u8 rawframe[]; /* frame_size = H / 4 * vbandlength */ } __packed; /* intermediate buffers with raw data from the USB cam */ -- cgit v1.2.3 From 6fce59ac719f9adeeed2556ed74e23a607f931e8 Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Fri, 12 Jun 2020 12:56:02 +0200 Subject: media: atmel: atmel-sama5d2-isc: fix warning in configs without OF All warnings (new ones prefixed by >>, old ones prefixed by <<): >> drivers/media/platform/atmel/atmel-sama5d2-isc.c:323:34: warning: unused variable 'atmel_isc_of_match' [-Wunused-const-variable] static const struct of_device_id atmel_isc_of_match[] = { ^ 1 warning generated. vim +/atmel_isc_of_match +323 drivers/media/platform/atmel/atmel-sama5d2-isc.c 322 > 323 static const struct of_device_id atmel_isc_of_match[] = { 324 { .compatible = "atmel,sama5d2-isc" }, 325 { } 326 }; 327 MODULE_DEVICE_TABLE(of, atmel_isc_of_match); 328 Fixed warning by guarding the atmel_isc_of_match by CONFIG_OF. Reported-by: kernel test robot Signed-off-by: Eugen Hristev Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/atmel/atmel-sama5d2-isc.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/atmel/atmel-sama5d2-isc.c b/drivers/media/platform/atmel/atmel-sama5d2-isc.c index 78381651238d..a3304f49e499 100644 --- a/drivers/media/platform/atmel/atmel-sama5d2-isc.c +++ b/drivers/media/platform/atmel/atmel-sama5d2-isc.c @@ -321,11 +321,13 @@ static const struct dev_pm_ops atmel_isc_dev_pm_ops = { SET_RUNTIME_PM_OPS(isc_runtime_suspend, isc_runtime_resume, NULL) }; +#if IS_ENABLED(CONFIG_OF) static const struct of_device_id atmel_isc_of_match[] = { { .compatible = "atmel,sama5d2-isc" }, { } }; MODULE_DEVICE_TABLE(of, atmel_isc_of_match); +#endif static struct platform_driver atmel_isc_driver = { .probe = atmel_isc_probe, -- cgit v1.2.3 From 20171723144ca0d057b72e852536992fd371369a Mon Sep 17 00:00:00 2001 From: Chen Zhou Date: Sat, 9 May 2020 04:07:11 +0200 Subject: media: coda: jpeg: add NULL check after kmalloc Fixes coccicheck warning: ./drivers/media/platform/coda/coda-jpeg.c:331:3-31: alloc with no test, possible model on line 354 Add NULL check after kmalloc. Signed-off-by: Chen Zhou Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-jpeg.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/coda/coda-jpeg.c b/drivers/media/platform/coda/coda-jpeg.c index 00d19859db50..b11cfbe166dd 100644 --- a/drivers/media/platform/coda/coda-jpeg.c +++ b/drivers/media/platform/coda/coda-jpeg.c @@ -327,8 +327,11 @@ int coda_jpeg_decode_header(struct coda_ctx *ctx, struct vb2_buffer *vb) "only 8-bit quantization tables supported\n"); continue; } - if (!ctx->params.jpeg_qmat_tab[i]) + if (!ctx->params.jpeg_qmat_tab[i]) { ctx->params.jpeg_qmat_tab[i] = kmalloc(64, GFP_KERNEL); + if (!ctx->params.jpeg_qmat_tab[i]) + return -ENOMEM; + } memcpy(ctx->params.jpeg_qmat_tab[i], quantization_tables[i].start, 64); } -- cgit v1.2.3 From 2505a210fc126599013aec2be741df20aaacc490 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 8 May 2020 16:40:22 +0200 Subject: media: firewire: Using uninitialized values in node_probe() If fw_csr_string() returns -ENOENT, then "name" is uninitialized. So then the "strlen(model_names[i]) <= name_len" is true because strlen() is unsigned and -ENOENT is type promoted to a very high positive value. Then the "strncmp(name, model_names[i], name_len)" uses uninitialized data because "name" is uninitialized. Fixes: 92374e886c75 ("[media] firedtv: drop obsolete backend abstraction") Signed-off-by: Dan Carpenter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/firewire/firedtv-fw.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/firewire/firedtv-fw.c b/drivers/media/firewire/firedtv-fw.c index 97144734eb05..3f1ca40b9b98 100644 --- a/drivers/media/firewire/firedtv-fw.c +++ b/drivers/media/firewire/firedtv-fw.c @@ -272,6 +272,8 @@ static int node_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) name_len = fw_csr_string(unit->directory, CSR_MODEL, name, sizeof(name)); + if (name_len < 0) + return name_len; for (i = ARRAY_SIZE(model_names); --i; ) if (strlen(model_names[i]) <= name_len && strncmp(name, model_names[i], name_len) == 0) -- cgit v1.2.3 From 0d1e342a17f5b10e2231fc8f98cac7b88b1b6897 Mon Sep 17 00:00:00 2001 From: Francois Buergisser Date: Wed, 6 May 2020 07:01:37 +0200 Subject: media: mtk-mdp: Remove states for format checks The mtk-mdp driver uses states to check if the formats have been set on the capture and output when turning the streaming on, setting controls or setting the selection rectangles. Those states are reset when 0 buffers are requested like when checking capabilities. This patch removes all format checks and set one by default as queues in V4L2 are expected to always have a format set. https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/vidioc-streamon.html https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/vidioc-g-ctrl.html https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/vidioc-g-selection.html Signed-off-by: Francois Buergisser Signed-off-by: Eizan Miyamoto Reviewed-by: Enric Balletbo I Serra Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-mdp/mtk_mdp_core.h | 2 - drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c | 90 ++++++++++----------------- 2 files changed, 34 insertions(+), 58 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.h b/drivers/media/platform/mtk-mdp/mtk_mdp_core.h index bafcccd71f31..dd130cc218c9 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.h +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.h @@ -28,8 +28,6 @@ #define MTK_MDP_FMT_FLAG_CAPTURE BIT(1) #define MTK_MDP_VPU_INIT BIT(0) -#define MTK_MDP_SRC_FMT BIT(1) -#define MTK_MDP_DST_FMT BIT(2) #define MTK_MDP_CTX_ERROR BIT(5) /** diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c index 821f2cf325f0..bb9caaf513bc 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c @@ -369,13 +369,6 @@ void mtk_mdp_ctx_state_lock_set(struct mtk_mdp_ctx *ctx, u32 state) mutex_unlock(&ctx->slock); } -static void mtk_mdp_ctx_state_lock_clear(struct mtk_mdp_ctx *ctx, u32 state) -{ - mutex_lock(&ctx->slock); - ctx->state &= ~state; - mutex_unlock(&ctx->slock); -} - static bool mtk_mdp_ctx_state_is_set(struct mtk_mdp_ctx *ctx, u32 mask) { bool ret; @@ -726,11 +719,6 @@ static int mtk_mdp_m2m_s_fmt_mplane(struct file *file, void *fh, ctx->quant = pix_mp->quantization; } - if (V4L2_TYPE_IS_OUTPUT(f->type)) - mtk_mdp_ctx_state_lock_set(ctx, MTK_MDP_SRC_FMT); - else - mtk_mdp_ctx_state_lock_set(ctx, MTK_MDP_DST_FMT); - mtk_mdp_dbg(2, "[%d] type:%d, frame:%dx%d", ctx->id, f->type, frame->width, frame->height); @@ -742,13 +730,6 @@ static int mtk_mdp_m2m_reqbufs(struct file *file, void *fh, { struct mtk_mdp_ctx *ctx = fh_to_ctx(fh); - if (reqbufs->count == 0) { - if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - mtk_mdp_ctx_state_lock_clear(ctx, MTK_MDP_SRC_FMT); - else - mtk_mdp_ctx_state_lock_clear(ctx, MTK_MDP_DST_FMT); - } - return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); } @@ -758,14 +739,6 @@ static int mtk_mdp_m2m_streamon(struct file *file, void *fh, struct mtk_mdp_ctx *ctx = fh_to_ctx(fh); int ret; - /* The source and target color format need to be set */ - if (V4L2_TYPE_IS_OUTPUT(type)) { - if (!mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_SRC_FMT)) - return -EINVAL; - } else if (!mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_DST_FMT)) { - return -EINVAL; - } - if (!mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_VPU_INIT)) { ret = mtk_mdp_vpu_init(&ctx->vpu); if (ret < 0) { @@ -899,24 +872,21 @@ static int mtk_mdp_m2m_s_selection(struct file *file, void *fh, frame = &ctx->d_frame; /* Check to see if scaling ratio is within supported range */ - if (mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_DST_FMT | MTK_MDP_SRC_FMT)) { - if (V4L2_TYPE_IS_OUTPUT(s->type)) { - ret = mtk_mdp_check_scaler_ratio(variant, new_r.width, - new_r.height, ctx->d_frame.crop.width, - ctx->d_frame.crop.height, - ctx->ctrls.rotate->val); - } else { - ret = mtk_mdp_check_scaler_ratio(variant, - ctx->s_frame.crop.width, - ctx->s_frame.crop.height, new_r.width, - new_r.height, ctx->ctrls.rotate->val); - } + if (V4L2_TYPE_IS_OUTPUT(s->type)) + ret = mtk_mdp_check_scaler_ratio(variant, new_r.width, + new_r.height, ctx->d_frame.crop.width, + ctx->d_frame.crop.height, + ctx->ctrls.rotate->val); + else + ret = mtk_mdp_check_scaler_ratio(variant, + ctx->s_frame.crop.width, + ctx->s_frame.crop.height, new_r.width, + new_r.height, ctx->ctrls.rotate->val); - if (ret) { - dev_info(&ctx->mdp_dev->pdev->dev, - "Out of scaler range"); - return -EINVAL; - } + if (ret) { + dev_info(&ctx->mdp_dev->pdev->dev, + "Out of scaler range"); + return -EINVAL; } s->r = new_r; @@ -989,7 +959,6 @@ static int mtk_mdp_s_ctrl(struct v4l2_ctrl *ctrl) struct mtk_mdp_ctx *ctx = ctrl_to_ctx(ctrl); struct mtk_mdp_dev *mdp = ctx->mdp_dev; struct mtk_mdp_variant *variant = mdp->variant; - u32 state = MTK_MDP_DST_FMT | MTK_MDP_SRC_FMT; int ret = 0; if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) @@ -1003,17 +972,15 @@ static int mtk_mdp_s_ctrl(struct v4l2_ctrl *ctrl) ctx->vflip = ctrl->val; break; case V4L2_CID_ROTATE: - if (mtk_mdp_ctx_state_is_set(ctx, state)) { - ret = mtk_mdp_check_scaler_ratio(variant, - ctx->s_frame.crop.width, - ctx->s_frame.crop.height, - ctx->d_frame.crop.width, - ctx->d_frame.crop.height, - ctx->ctrls.rotate->val); - - if (ret) - return -EINVAL; - } + ret = mtk_mdp_check_scaler_ratio(variant, + ctx->s_frame.crop.width, + ctx->s_frame.crop.height, + ctx->d_frame.crop.width, + ctx->d_frame.crop.height, + ctx->ctrls.rotate->val); + + if (ret) + return -EINVAL; ctx->rotation = ctrl->val; break; @@ -1090,6 +1057,7 @@ static int mtk_mdp_m2m_open(struct file *file) struct video_device *vfd = video_devdata(file); struct mtk_mdp_ctx *ctx = NULL; int ret; + struct v4l2_format default_format; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -1144,6 +1112,16 @@ static int mtk_mdp_m2m_open(struct file *file) list_add(&ctx->list, &mdp->ctx_list); mutex_unlock(&mdp->lock); + /* Default format */ + memset(&default_format, 0, sizeof(default_format)); + default_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + default_format.fmt.pix_mp.width = 32; + default_format.fmt.pix_mp.height = 32; + default_format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420M; + mtk_mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format); + default_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + mtk_mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format); + mtk_mdp_dbg(0, "%s [%d]", dev_name(&mdp->pdev->dev), ctx->id); return 0; -- cgit v1.2.3 From 86ef5146fe49bcf0801fba199d98a37151ed3791 Mon Sep 17 00:00:00 2001 From: Eizan Miyamoto Date: Thu, 7 May 2020 12:23:42 +0200 Subject: media: mtk-mdp: remove mtk_mdp_comp.regs from mtk_mdp_comp.h These fields are not used and can be removed. Signed-off-by: Eizan Miyamoto Reviewed-by: Enric Balletbo I Serra Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-mdp/mtk_mdp_comp.c | 1 - drivers/media/platform/mtk-mdp/mtk_mdp_comp.h | 2 -- 2 files changed, 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c index 58abfbdfb82d..c76cd61fb178 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c @@ -106,7 +106,6 @@ int mtk_mdp_comp_init(struct device *dev, struct device_node *node, comp->dev_node = of_node_get(node); comp->id = comp_id; comp->type = mtk_mdp_matches[comp_id].type; - comp->regs = of_iomap(node, 0); for (i = 0; i < ARRAY_SIZE(comp->clk); i++) { comp->clk[i] = of_clk_get(node, i); diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h index 998a4b953025..3b83bd6e0d8b 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h @@ -38,7 +38,6 @@ enum mtk_mdp_comp_id { * struct mtk_mdp_comp - the MDP's function component data * @dev_node: component device node * @clk: clocks required for component - * @regs: Mapped address of component registers. * @larb_dev: SMI device required for component * @type: component type * @id: component ID @@ -46,7 +45,6 @@ enum mtk_mdp_comp_id { struct mtk_mdp_comp { struct device_node *dev_node; struct clk *clk[2]; - void __iomem *regs; struct device *larb_dev; enum mtk_mdp_comp_type type; enum mtk_mdp_comp_id id; -- cgit v1.2.3 From 256a12e1a343e1efff00a9065063f0846d219336 Mon Sep 17 00:00:00 2001 From: Eizan Miyamoto Date: Thu, 7 May 2020 12:23:44 +0200 Subject: media: mtk-mdp: handle vb2_dma_contig_set_max_seg_size errors during probe This is a cleanup to better handle errors during MDP probe. Signed-off-by: Eizan Miyamoto Reviewed-by: Enric Balletbo i Serra Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-mdp/mtk_mdp_core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c index aeaed2cf4458..9b24b8d46eb7 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c @@ -193,7 +193,11 @@ static int mtk_mdp_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mdp); - vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); + ret = vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "Failed to set vb2 dma mag seg size\n"); + goto err_m2m_register; + } pm_runtime_enable(dev); dev_dbg(dev, "mdp-%d registered successfully\n", mdp->id); -- cgit v1.2.3 From ee18fc7b0b9517ac62711da1b95095f122aead94 Mon Sep 17 00:00:00 2001 From: Eizan Miyamoto Date: Thu, 7 May 2020 12:23:46 +0200 Subject: media: mtk-mdp: handle vpu_wdt_reg_handler() errors during probe This is a cleanup to better handle errors during MDP probe. Signed-off-by: Eizan Miyamoto Reviewed-by: Enric Balletbo i Serra Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-mdp/mtk_mdp_core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c index 9b24b8d46eb7..17d155219ba2 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c @@ -188,8 +188,12 @@ static int mtk_mdp_probe(struct platform_device *pdev) } mdp->vpu_dev = vpu_get_plat_device(pdev); - vpu_wdt_reg_handler(mdp->vpu_dev, mtk_mdp_reset_handler, mdp, - VPU_RST_MDP); + ret = vpu_wdt_reg_handler(mdp->vpu_dev, mtk_mdp_reset_handler, mdp, + VPU_RST_MDP); + if (ret) { + dev_err(&pdev->dev, "Failed to register reset handler\n"); + goto err_m2m_register; + } platform_set_drvdata(pdev, mdp); -- cgit v1.2.3 From 86698b9505bbc97b1cd6d8e9a2e7b8604a079469 Mon Sep 17 00:00:00 2001 From: Eizan Miyamoto Date: Thu, 7 May 2020 12:23:48 +0200 Subject: media: mtk-mdp: convert mtk_mdp_dev.comp array to list The functions mtk_mdp_register/unregister_component have been created to add / remove items from the list of components. This will eventually enable us to specify a list of components in the device tree instead of hardcoding them into this driver. The list is modified by a single thread at driver probe time, and will not be traversed by another thread until the call to pm_runtime_enable at the end of probing. Signed-off-by: Eizan Miyamoto Reviewed-by: Enric Balletbo I Serra Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-mdp/mtk_mdp_comp.c | 1 + drivers/media/platform/mtk-mdp/mtk_mdp_comp.h | 2 ++ drivers/media/platform/mtk-mdp/mtk_mdp_core.c | 46 +++++++++++++++++++-------- drivers/media/platform/mtk-mdp/mtk_mdp_core.h | 10 ++++-- 4 files changed, 43 insertions(+), 16 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c index c76cd61fb178..da2bdad7a8d1 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c @@ -103,6 +103,7 @@ int mtk_mdp_comp_init(struct device *dev, struct device_node *node, return -EINVAL; } + INIT_LIST_HEAD(&comp->node); comp->dev_node = of_node_get(node); comp->id = comp_id; comp->type = mtk_mdp_matches[comp_id].type; diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h index 3b83bd6e0d8b..1f745891c6c3 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h @@ -36,6 +36,7 @@ enum mtk_mdp_comp_id { /** * struct mtk_mdp_comp - the MDP's function component data + * @node: list node to track sibing MDP components * @dev_node: component device node * @clk: clocks required for component * @larb_dev: SMI device required for component @@ -43,6 +44,7 @@ enum mtk_mdp_comp_id { * @id: component ID */ struct mtk_mdp_comp { + struct list_head node; struct device_node *dev_node; struct clk *clk[2]; struct device *larb_dev; diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c index 17d155219ba2..40b9fda8b03b 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c @@ -55,19 +55,19 @@ MODULE_DEVICE_TABLE(of, mtk_mdp_of_ids); static void mtk_mdp_clock_on(struct mtk_mdp_dev *mdp) { struct device *dev = &mdp->pdev->dev; - int i; + struct mtk_mdp_comp *comp_node; - for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) - mtk_mdp_comp_clock_on(dev, mdp->comp[i]); + list_for_each_entry(comp_node, &mdp->comp_list, node) + mtk_mdp_comp_clock_on(dev, comp_node); } static void mtk_mdp_clock_off(struct mtk_mdp_dev *mdp) { struct device *dev = &mdp->pdev->dev; - int i; + struct mtk_mdp_comp *comp_node; - for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) - mtk_mdp_comp_clock_off(dev, mdp->comp[i]); + list_for_each_entry(comp_node, &mdp->comp_list, node) + mtk_mdp_comp_clock_off(dev, comp_node); } static void mtk_mdp_wdt_worker(struct work_struct *work) @@ -91,12 +91,25 @@ static void mtk_mdp_reset_handler(void *priv) queue_work(mdp->wdt_wq, &mdp->wdt_work); } +void mtk_mdp_register_component(struct mtk_mdp_dev *mdp, + struct mtk_mdp_comp *comp) +{ + list_add(&mdp->comp_list, &comp->node); +} + +void mtk_mdp_unregister_component(struct mtk_mdp_dev *mdp, + struct mtk_mdp_comp *comp) +{ + list_del(&comp->node); +} + static int mtk_mdp_probe(struct platform_device *pdev) { struct mtk_mdp_dev *mdp; struct device *dev = &pdev->dev; struct device_node *node, *parent; - int i, ret = 0; + struct mtk_mdp_comp *comp, *comp_temp; + int ret = 0; mdp = devm_kzalloc(dev, sizeof(*mdp), GFP_KERNEL); if (!mdp) @@ -104,6 +117,7 @@ static int mtk_mdp_probe(struct platform_device *pdev) mdp->id = pdev->id; mdp->pdev = pdev; + INIT_LIST_HEAD(&mdp->comp_list); INIT_LIST_HEAD(&mdp->ctx_list); mutex_init(&mdp->lock); @@ -124,7 +138,6 @@ static int mtk_mdp_probe(struct platform_device *pdev) const struct of_device_id *of_id; enum mtk_mdp_comp_type comp_type; int comp_id; - struct mtk_mdp_comp *comp; of_id = of_match_node(mtk_mdp_comp_dt_ids, node); if (!of_id) @@ -150,13 +163,14 @@ static int mtk_mdp_probe(struct platform_device *pdev) of_node_put(node); goto err_comp; } - mdp->comp[comp_id] = comp; ret = mtk_mdp_comp_init(dev, node, comp, comp_id); if (ret) { of_node_put(node); goto err_comp; } + + mtk_mdp_register_component(mdp, comp); } mdp->job_wq = create_singlethread_workqueue(MTK_MDP_MODULE_NAME); @@ -220,8 +234,10 @@ err_alloc_wdt_wq: err_alloc_job_wq: err_comp: - for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) - mtk_mdp_comp_deinit(dev, mdp->comp[i]); + list_for_each_entry_safe(comp, comp_temp, &mdp->comp_list, node) { + mtk_mdp_unregister_component(mdp, comp); + mtk_mdp_comp_deinit(dev, comp); + } dev_dbg(dev, "err %d\n", ret); return ret; @@ -230,7 +246,7 @@ err_comp: static int mtk_mdp_remove(struct platform_device *pdev) { struct mtk_mdp_dev *mdp = platform_get_drvdata(pdev); - int i; + struct mtk_mdp_comp *comp, *comp_temp; pm_runtime_disable(&pdev->dev); vb2_dma_contig_clear_max_seg_size(&pdev->dev); @@ -243,8 +259,10 @@ static int mtk_mdp_remove(struct platform_device *pdev) flush_workqueue(mdp->job_wq); destroy_workqueue(mdp->job_wq); - for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) - mtk_mdp_comp_deinit(&pdev->dev, mdp->comp[i]); + list_for_each_entry_safe(comp, comp_temp, &mdp->comp_list, node) { + mtk_mdp_unregister_component(mdp, comp); + mtk_mdp_comp_deinit(&pdev->dev, comp); + } dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name); return 0; diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.h b/drivers/media/platform/mtk-mdp/mtk_mdp_core.h index dd130cc218c9..a7da14b97077 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.h +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.h @@ -136,7 +136,7 @@ struct mtk_mdp_variant { * @pdev: pointer to the image processor platform device * @variant: the IP variant information * @id: image processor device index (0..MTK_MDP_MAX_DEVS) - * @comp: MDP function components + * @comp_list: list of MDP function components * @m2m_dev: v4l2 memory-to-memory device data * @ctx_list: list of struct mtk_mdp_ctx * @vdev: video device for image processor driver @@ -154,7 +154,7 @@ struct mtk_mdp_dev { struct platform_device *pdev; struct mtk_mdp_variant *variant; u16 id; - struct mtk_mdp_comp *comp[MTK_MDP_COMP_ID_MAX]; + struct list_head comp_list; struct v4l2_m2m_dev *m2m_dev; struct list_head ctx_list; struct video_device *vdev; @@ -221,6 +221,12 @@ struct mtk_mdp_ctx { extern int mtk_mdp_dbg_level; +void mtk_mdp_register_component(struct mtk_mdp_dev *mdp, + struct mtk_mdp_comp *comp); + +void mtk_mdp_unregister_component(struct mtk_mdp_dev *mdp, + struct mtk_mdp_comp *comp); + #if defined(DEBUG) #define mtk_mdp_dbg(level, fmt, args...) \ -- cgit v1.2.3 From 37e278c801078dcf165983a83f7cbefbae856004 Mon Sep 17 00:00:00 2001 From: Eizan Miyamoto Date: Thu, 7 May 2020 12:23:50 +0200 Subject: media: mtk-mdp: Remove mtk_mdp_comp.id and supporting functionality Since components are registered in a list, the numeric component id that specified a location in an array is not necessary. Signed-off-by: Eizan Miyamoto Reviewed-by: Enric Balletbo i Serra Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-mdp/mtk_mdp_comp.c | 60 ++++----------------------- drivers/media/platform/mtk-mdp/mtk_mdp_comp.h | 19 +-------- drivers/media/platform/mtk-mdp/mtk_mdp_core.c | 9 +--- 3 files changed, 10 insertions(+), 78 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c index da2bdad7a8d1..362fff924aef 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c @@ -14,46 +14,6 @@ #include "mtk_mdp_comp.h" -static const char * const mtk_mdp_comp_stem[MTK_MDP_COMP_TYPE_MAX] = { - "mdp-rdma", - "mdp-rsz", - "mdp-wdma", - "mdp-wrot", -}; - -struct mtk_mdp_comp_match { - enum mtk_mdp_comp_type type; - int alias_id; -}; - -static const struct mtk_mdp_comp_match mtk_mdp_matches[MTK_MDP_COMP_ID_MAX] = { - { MTK_MDP_RDMA, 0 }, - { MTK_MDP_RDMA, 1 }, - { MTK_MDP_RSZ, 0 }, - { MTK_MDP_RSZ, 1 }, - { MTK_MDP_RSZ, 2 }, - { MTK_MDP_WDMA, 0 }, - { MTK_MDP_WROT, 0 }, - { MTK_MDP_WROT, 1 }, -}; - -int mtk_mdp_comp_get_id(struct device *dev, struct device_node *node, - enum mtk_mdp_comp_type comp_type) -{ - int id = of_alias_get_id(node, mtk_mdp_comp_stem[comp_type]); - int i; - - for (i = 0; i < ARRAY_SIZE(mtk_mdp_matches); i++) { - if (comp_type == mtk_mdp_matches[i].type && - id == mtk_mdp_matches[i].alias_id) - return i; - } - - dev_err(dev, "Failed to get id. type: %d, id: %d\n", comp_type, id); - - return -EINVAL; -} - void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp) { int i, err; @@ -62,8 +22,8 @@ void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp) err = mtk_smi_larb_get(comp->larb_dev); if (err) dev_err(dev, - "failed to get larb, err %d. type:%d id:%d\n", - err, comp->type, comp->id); + "failed to get larb, err %d. type:%d\n", + err, comp->type); } for (i = 0; i < ARRAY_SIZE(comp->clk); i++) { @@ -72,8 +32,8 @@ void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp) err = clk_prepare_enable(comp->clk[i]); if (err) dev_err(dev, - "failed to enable clock, err %d. type:%d id:%d i:%d\n", - err, comp->type, comp->id, i); + "failed to enable clock, err %d. type:%d i:%d\n", + err, comp->type, i); } } @@ -92,21 +52,15 @@ void mtk_mdp_comp_clock_off(struct device *dev, struct mtk_mdp_comp *comp) } int mtk_mdp_comp_init(struct device *dev, struct device_node *node, - struct mtk_mdp_comp *comp, enum mtk_mdp_comp_id comp_id) + struct mtk_mdp_comp *comp, + enum mtk_mdp_comp_type comp_type) { struct device_node *larb_node; struct platform_device *larb_pdev; int i; - if (comp_id < 0 || comp_id >= MTK_MDP_COMP_ID_MAX) { - dev_err(dev, "Invalid comp_id %d\n", comp_id); - return -EINVAL; - } - - INIT_LIST_HEAD(&comp->node); comp->dev_node = of_node_get(node); - comp->id = comp_id; - comp->type = mtk_mdp_matches[comp_id].type; + comp->type = comp_type; for (i = 0; i < ARRAY_SIZE(comp->clk); i++) { comp->clk[i] = of_clk_get(node, i); diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h index 1f745891c6c3..1bf0242cce46 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h @@ -22,18 +22,6 @@ enum mtk_mdp_comp_type { MTK_MDP_COMP_TYPE_MAX, }; -enum mtk_mdp_comp_id { - MTK_MDP_COMP_RDMA0, - MTK_MDP_COMP_RDMA1, - MTK_MDP_COMP_RSZ0, - MTK_MDP_COMP_RSZ1, - MTK_MDP_COMP_RSZ2, - MTK_MDP_COMP_WDMA, - MTK_MDP_COMP_WROT0, - MTK_MDP_COMP_WROT1, - MTK_MDP_COMP_ID_MAX, -}; - /** * struct mtk_mdp_comp - the MDP's function component data * @node: list node to track sibing MDP components @@ -41,7 +29,6 @@ enum mtk_mdp_comp_id { * @clk: clocks required for component * @larb_dev: SMI device required for component * @type: component type - * @id: component ID */ struct mtk_mdp_comp { struct list_head node; @@ -49,14 +36,12 @@ struct mtk_mdp_comp { struct clk *clk[2]; struct device *larb_dev; enum mtk_mdp_comp_type type; - enum mtk_mdp_comp_id id; }; int mtk_mdp_comp_init(struct device *dev, struct device_node *node, - struct mtk_mdp_comp *comp, enum mtk_mdp_comp_id comp_id); + struct mtk_mdp_comp *comp, + enum mtk_mdp_comp_type comp_type); void mtk_mdp_comp_deinit(struct device *dev, struct mtk_mdp_comp *comp); -int mtk_mdp_comp_get_id(struct device *dev, struct device_node *node, - enum mtk_mdp_comp_type comp_type); void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp); void mtk_mdp_comp_clock_off(struct device *dev, struct mtk_mdp_comp *comp); diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c index 40b9fda8b03b..f96c8b3bf861 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c @@ -137,7 +137,6 @@ static int mtk_mdp_probe(struct platform_device *pdev) for_each_child_of_node(parent, node) { const struct of_device_id *of_id; enum mtk_mdp_comp_type comp_type; - int comp_id; of_id = of_match_node(mtk_mdp_comp_dt_ids, node); if (!of_id) @@ -150,12 +149,6 @@ static int mtk_mdp_probe(struct platform_device *pdev) } comp_type = (enum mtk_mdp_comp_type)of_id->data; - comp_id = mtk_mdp_comp_get_id(dev, node, comp_type); - if (comp_id < 0) { - dev_warn(dev, "Skipping unknown component %pOF\n", - node); - continue; - } comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL); if (!comp) { @@ -164,7 +157,7 @@ static int mtk_mdp_probe(struct platform_device *pdev) goto err_comp; } - ret = mtk_mdp_comp_init(dev, node, comp, comp_id); + ret = mtk_mdp_comp_init(dev, node, comp, comp_type); if (ret) { of_node_put(node); goto err_comp; -- cgit v1.2.3 From 414953b4833f2b2024ec46213a75586465acea90 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 24 Apr 2020 13:25:54 +0200 Subject: media: cx231xx: remove redundant assignment to variable err The variable err is being initialized with a value that is never read and it is being updated later with a new value. The initialization is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cx231xx/cx231xx-417.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index b0cd51134654..c5e21785fafe 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -1734,7 +1734,7 @@ static void cx231xx_video_dev_init( int cx231xx_417_register(struct cx231xx *dev) { /* FIXME: Port1 hardcoded here */ - int err = -ENODEV; + int err; struct cx231xx_tsport *tsport = &dev->ts1; struct vb2_queue *q; -- cgit v1.2.3 From c92d30e4b78dc331909f8c6056c2792aa14e2166 Mon Sep 17 00:00:00 2001 From: Eugeniu Rosca Date: Tue, 2 Jun 2020 21:50:16 +0200 Subject: media: vsp1: dl: Fix NULL pointer dereference on unbind In commit f3b98e3c4d2e16 ("media: vsp1: Provide support for extended command pools"), the vsp pointer used for referencing the VSP1 device structure from a command pool during vsp1_dl_ext_cmd_pool_destroy() was not populated. Correctly assign the pointer to prevent the following null-pointer-dereference when removing the device: [*] h3ulcb-kf #> echo fea28000.vsp > /sys/bus/platform/devices/fea28000.vsp/driver/unbind Unable to handle kernel NULL pointer dereference at virtual address 0000000000000028 Mem abort info: ESR = 0x96000006 EC = 0x25: DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 Data abort info: ISV = 0, ISS = 0x00000006 CM = 0, WnR = 0 user pgtable: 4k pages, 48-bit VAs, pgdp=00000007318be000 [0000000000000028] pgd=00000007333a1003, pud=00000007333a6003, pmd=0000000000000000 Internal error: Oops: 96000006 [#1] PREEMPT SMP Modules linked in: CPU: 1 PID: 486 Comm: sh Not tainted 5.7.0-rc6-arm64-renesas-00118-ge644645abf47 #185 Hardware name: Renesas H3ULCB Kingfisher board based on r8a77951 (DT) pstate: 40000005 (nZcv daif -PAN -UAO) pc : vsp1_dlm_destroy+0xe4/0x11c lr : vsp1_dlm_destroy+0xc8/0x11c sp : ffff800012963b60 x29: ffff800012963b60 x28: ffff0006f83fc440 x27: 0000000000000000 x26: ffff0006f5e13e80 x25: ffff0006f5e13ed0 x24: ffff0006f5e13ed0 x23: ffff0006f5e13ed0 x22: dead000000000122 x21: ffff0006f5e3a080 x20: ffff0006f5df2938 x19: ffff0006f5df2980 x18: 0000000000000003 x17: 0000000000000000 x16: 0000000000000016 x15: 0000000000000003 x14: 00000000000393c0 x13: ffff800011a5ec18 x12: ffff800011d8d000 x11: ffff0006f83fcc68 x10: ffff800011a53d70 x9 : ffff8000111f3000 x8 : 0000000000000000 x7 : 0000000000210d00 x6 : 0000000000000000 x5 : ffff800010872e60 x4 : 0000000000000004 x3 : 0000000078068000 x2 : ffff800012781000 x1 : 0000000000002c00 x0 : 0000000000000000 Call trace: vsp1_dlm_destroy+0xe4/0x11c vsp1_wpf_destroy+0x10/0x20 vsp1_entity_destroy+0x24/0x4c vsp1_destroy_entities+0x54/0x130 vsp1_remove+0x1c/0x40 platform_drv_remove+0x28/0x50 __device_release_driver+0x178/0x220 device_driver_detach+0x44/0xc0 unbind_store+0xe0/0x104 drv_attr_store+0x20/0x30 sysfs_kf_write+0x48/0x70 kernfs_fop_write+0x148/0x230 __vfs_write+0x18/0x40 vfs_write+0xdc/0x1c4 ksys_write+0x68/0xf0 __arm64_sys_write+0x18/0x20 el0_svc_common.constprop.0+0x70/0x170 do_el0_svc+0x20/0x80 el0_sync_handler+0x134/0x1b0 el0_sync+0x140/0x180 Code: b40000c2 f9403a60 d2800084 a9400663 (f9401400) ---[ end trace 3875369841fb288a ]--- Fixes: f3b98e3c4d2e16 ("media: vsp1: Provide support for extended command pools") Cc: stable@vger.kernel.org # v4.19+ Signed-off-by: Eugeniu Rosca Reviewed-by: Kieran Bingham Tested-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_dl.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index d7b43037e500..e07b135613eb 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -431,6 +431,8 @@ vsp1_dl_cmd_pool_create(struct vsp1_device *vsp1, enum vsp1_extcmd_type type, if (!pool) return NULL; + pool->vsp1 = vsp1; + spin_lock_init(&pool->lock); INIT_LIST_HEAD(&pool->free); -- cgit v1.2.3 From 98f803cfa76eb67d0e429ba76a39471f95d83675 Mon Sep 17 00:00:00 2001 From: Jeff Chase Date: Tue, 23 Jun 2020 01:59:49 +0200 Subject: media: cec: add adap_controls_phys_addr option Use of the cec notifier framework is required to support CEC_CAP_CONNECTOR_INFO but some devices do not want physical address updates from the notifier. This adds an option to allow registering with a cec notifier without getting address updates. [hans: document the new adap_controls_phys_addr bool] Signed-off-by: Jeff Chase Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/core/cec-notifier.c | 11 +++++++---- include/media/cec.h | 6 ++++++ 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/cec/core/cec-notifier.c b/drivers/media/cec/core/cec-notifier.c index 517e0035fc99..95f363bb1d19 100644 --- a/drivers/media/cec/core/cec-notifier.c +++ b/drivers/media/cec/core/cec-notifier.c @@ -116,7 +116,8 @@ cec_notifier_conn_register(struct device *hdmi_dev, const char *port_name, else memset(&n->conn_info, 0, sizeof(n->conn_info)); if (n->cec_adap) { - cec_phys_addr_invalidate(n->cec_adap); + if (!n->cec_adap->adap_controls_phys_addr) + cec_phys_addr_invalidate(n->cec_adap); cec_s_conn_info(n->cec_adap, conn_info); } mutex_unlock(&n->lock); @@ -133,7 +134,8 @@ void cec_notifier_conn_unregister(struct cec_notifier *n) memset(&n->conn_info, 0, sizeof(n->conn_info)); n->phys_addr = CEC_PHYS_ADDR_INVALID; if (n->cec_adap) { - cec_phys_addr_invalidate(n->cec_adap); + if (!n->cec_adap->adap_controls_phys_addr) + cec_phys_addr_invalidate(n->cec_adap); cec_s_conn_info(n->cec_adap, NULL); } mutex_unlock(&n->lock); @@ -158,7 +160,8 @@ cec_notifier_cec_adap_register(struct device *hdmi_dev, const char *port_name, n->cec_adap = adap; adap->conn_info = n->conn_info; adap->notifier = n; - cec_s_phys_addr(adap, n->phys_addr, false); + if (!adap->adap_controls_phys_addr) + cec_s_phys_addr(adap, n->phys_addr, false); mutex_unlock(&n->lock); return n; } @@ -185,7 +188,7 @@ void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa) mutex_lock(&n->lock); n->phys_addr = pa; - if (n->cec_adap) + if (n->cec_adap && !n->cec_adap->adap_controls_phys_addr) cec_s_phys_addr(n->cec_adap, n->phys_addr, false); mutex_unlock(&n->lock); } diff --git a/include/media/cec.h b/include/media/cec.h index 32f7c695d7b5..c48b5f2e4b50 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -172,6 +172,11 @@ struct cec_adap_ops { * @is_configured: the CEC adapter is configured (i.e. has claimed LAs) * @cec_pin_is_high: if true then the CEC pin is high. Only used with the * CEC pin framework. + * @adap_controls_phys_addr: if true, then the CEC adapter controls the + * physical address, i.e. the CEC hardware can detect HPD changes and + * read the EDID and is not dependent on an external HDMI driver. + * Drivers that need this can set this field to true after the + * cec_allocate_adapter() call. * @last_initiator: the initiator of the last transmitted message. * @monitor_all_cnt: number of filehandles monitoring all msgs * @monitor_pin_cnt: number of filehandles monitoring pin changes @@ -222,6 +227,7 @@ struct cec_adapter { bool is_configuring; bool is_configured; bool cec_pin_is_high; + bool adap_controls_phys_addr; u8 last_initiator; u32 monitor_all_cnt; u32 monitor_pin_cnt; -- cgit v1.2.3 From 21b9a47e0ec7997c5a762c102cc316ea0c23243f Mon Sep 17 00:00:00 2001 From: Jeff Chase Date: Tue, 23 Jun 2020 01:59:50 +0200 Subject: media: cec: i2c: ch7322: Add ch7322 CEC controller driver Add a CEC device driver for the Chrontel ch7322 CEC conroller. This is an I2C device capable of sending and receiving CEC messages. Signed-off-by: Jeff Chase Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 1 + drivers/media/cec/Kconfig | 1 + drivers/media/cec/Makefile | 2 +- drivers/media/cec/i2c/Kconfig | 14 + drivers/media/cec/i2c/Makefile | 5 + drivers/media/cec/i2c/ch7322.c | 604 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 626 insertions(+), 1 deletion(-) create mode 100644 drivers/media/cec/i2c/Kconfig create mode 100644 drivers/media/cec/i2c/Makefile create mode 100644 drivers/media/cec/i2c/ch7322.c (limited to 'drivers/media') diff --git a/MAINTAINERS b/MAINTAINERS index 9982c7c12af9..6362b96b7af3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4107,6 +4107,7 @@ L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/chontel,ch7322.yaml +F: drivers/media/cec/i2c/ch7322.c CIRRUS LOGIC AUDIO CODEC DRIVERS M: James Schulman diff --git a/drivers/media/cec/Kconfig b/drivers/media/cec/Kconfig index 7e830444bdbb..9ba3a00dce31 100644 --- a/drivers/media/cec/Kconfig +++ b/drivers/media/cec/Kconfig @@ -33,6 +33,7 @@ menuconfig MEDIA_CEC_SUPPORT adapter that supports HDMI CEC. if MEDIA_CEC_SUPPORT +source "drivers/media/cec/i2c/Kconfig" source "drivers/media/cec/platform/Kconfig" source "drivers/media/cec/usb/Kconfig" endif diff --git a/drivers/media/cec/Makefile b/drivers/media/cec/Makefile index 74e80e1b3571..23539339bc81 100644 --- a/drivers/media/cec/Makefile +++ b/drivers/media/cec/Makefile @@ -1,2 +1,2 @@ # SPDX-License-Identifier: GPL-2.0 -obj-y += core/ platform/ usb/ +obj-y += core/ i2c/ platform/ usb/ diff --git a/drivers/media/cec/i2c/Kconfig b/drivers/media/cec/i2c/Kconfig new file mode 100644 index 000000000000..70432a1d6918 --- /dev/null +++ b/drivers/media/cec/i2c/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# I2C drivers + +config CEC_CH7322 + tristate "Chrontel CH7322 CEC controller" + depends on I2C + select REGMAP_I2C + select CEC_CORE + help + This is a driver for the Chrontel CH7322 CEC controller. It uses the + generic CEC framework interface. + CEC bus is present in the HDMI connector and enables communication + between compatible devices. diff --git a/drivers/media/cec/i2c/Makefile b/drivers/media/cec/i2c/Makefile new file mode 100644 index 000000000000..d7496dfd0fa4 --- /dev/null +++ b/drivers/media/cec/i2c/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the CEC I2C device drivers. +# +obj-$(CONFIG_CEC_CH7322) += ch7322.o diff --git a/drivers/media/cec/i2c/ch7322.c b/drivers/media/cec/i2c/ch7322.c new file mode 100644 index 000000000000..0814338c43e4 --- /dev/null +++ b/drivers/media/cec/i2c/ch7322.c @@ -0,0 +1,604 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the Chrontel CH7322 CEC Controller + * + * Copyright 2020 Google LLC. + */ + +/* + * Notes + * + * - This device powers on in Auto Mode which has limited functionality. This + * driver disables Auto Mode when it attaches. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CH7322_WRITE 0x00 +#define CH7322_WRITE_MSENT 0x80 +#define CH7322_WRITE_BOK 0x40 +#define CH7322_WRITE_NMASK 0x0f + +/* Write buffer is 0x01-0x10 */ +#define CH7322_WRBUF 0x01 +#define CH7322_WRBUF_LEN 0x10 + +#define CH7322_READ 0x40 +#define CH7322_READ_NRDT 0x80 +#define CH7322_READ_MSENT 0x20 +#define CH7322_READ_NMASK 0x0f + +/* Read buffer is 0x41-0x50 */ +#define CH7322_RDBUF 0x41 +#define CH7322_RDBUF_LEN 0x10 + +#define CH7322_MODE 0x11 +#define CH7322_MODE_AUTO 0x78 +#define CH7322_MODE_SW 0xb5 + +#define CH7322_RESET 0x12 +#define CH7322_RESET_RST 0x00 + +#define CH7322_POWER 0x13 +#define CH7322_POWER_FPD 0x04 + +#define CH7322_CFG0 0x17 +#define CH7322_CFG0_EOBEN 0x40 +#define CH7322_CFG0_PEOB 0x20 +#define CH7322_CFG0_CLRSPP 0x10 +#define CH7322_CFG0_FLOW 0x08 + +#define CH7322_CFG1 0x1a +#define CH7322_CFG1_STDBYO 0x04 +#define CH7322_CFG1_HPBP 0x02 +#define CH7322_CFG1_PIO 0x01 + +#define CH7322_INTCTL 0x1b +#define CH7322_INTCTL_INTPB 0x80 +#define CH7322_INTCTL_STDBY 0x40 +#define CH7322_INTCTL_HPDFALL 0x20 +#define CH7322_INTCTL_HPDRISE 0x10 +#define CH7322_INTCTL_RXMSG 0x08 +#define CH7322_INTCTL_TXMSG 0x04 +#define CH7322_INTCTL_NEWPHA 0x02 +#define CH7322_INTCTL_ERROR 0x01 + +#define CH7322_DVCLKFNH 0x1d +#define CH7322_DVCLKFNL 0x1e + +#define CH7322_CTL 0x31 +#define CH7322_CTL_FSTDBY 0x80 +#define CH7322_CTL_PLSEN 0x40 +#define CH7322_CTL_PLSPB 0x20 +#define CH7322_CTL_SPADL 0x10 +#define CH7322_CTL_HINIT 0x08 +#define CH7322_CTL_WPHYA 0x04 +#define CH7322_CTL_H1T 0x02 +#define CH7322_CTL_S1T 0x01 + +#define CH7322_PAWH 0x32 +#define CH7322_PAWL 0x33 + +#define CH7322_ADDLW 0x34 +#define CH7322_ADDLW_MASK 0xf0 + +#define CH7322_ADDLR 0x3d +#define CH7322_ADDLR_HPD 0x80 +#define CH7322_ADDLR_MASK 0x0f + +#define CH7322_INTDATA 0x3e +#define CH7322_INTDATA_MODE 0x80 +#define CH7322_INTDATA_STDBY 0x40 +#define CH7322_INTDATA_HPDFALL 0x20 +#define CH7322_INTDATA_HPDRISE 0x10 +#define CH7322_INTDATA_RXMSG 0x08 +#define CH7322_INTDATA_TXMSG 0x04 +#define CH7322_INTDATA_NEWPHA 0x02 +#define CH7322_INTDATA_ERROR 0x01 + +#define CH7322_EVENT 0x3f +#define CH7322_EVENT_TXERR 0x80 +#define CH7322_EVENT_HRST 0x40 +#define CH7322_EVENT_HFST 0x20 +#define CH7322_EVENT_PHACHG 0x10 +#define CH7322_EVENT_ACTST 0x08 +#define CH7322_EVENT_PHARDY 0x04 +#define CH7322_EVENT_BSOK 0x02 +#define CH7322_EVENT_ERRADCF 0x01 + +#define CH7322_DID 0x51 +#define CH7322_DID_CH7322 0x5b +#define CH7322_DID_CH7323 0x5f + +#define CH7322_REVISIONID 0x52 + +#define CH7322_PARH 0x53 +#define CH7322_PARL 0x54 + +#define CH7322_IOCFG2 0x75 +#define CH7322_IOCFG_CIO 0x80 +#define CH7322_IOCFG_IOCFGMASK 0x78 +#define CH7322_IOCFG_AUDIO 0x04 +#define CH7322_IOCFG_SPAMST 0x02 +#define CH7322_IOCFG_SPAMSP 0x01 + +#define CH7322_CTL3 0x7b +#define CH7322_CTL3_SWENA 0x80 +#define CH7322_CTL3_FC_INIT 0x40 +#define CH7322_CTL3_SML_FL 0x20 +#define CH7322_CTL3_SM_RDST 0x10 +#define CH7322_CTL3_SPP_CIAH 0x08 +#define CH7322_CTL3_SPP_CIAL 0x04 +#define CH7322_CTL3_SPP_ACTH 0x02 +#define CH7322_CTL3_SPP_ACTL 0x01 + +/* BOK status means NACK */ +#define CH7322_TX_FLAG_NACK BIT(0) +/* Device will retry automatically */ +#define CH7322_TX_FLAG_RETRY BIT(1) + +struct ch7322 { + struct i2c_client *i2c; + struct regmap *regmap; + struct cec_adapter *cec; + struct mutex mutex; /* device access mutex */ + u8 tx_flags; +}; + +static const struct regmap_config ch7322_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x7f, + .disable_locking = true, +}; + +static int ch7322_send_message(struct ch7322 *ch7322, const struct cec_msg *msg) +{ + unsigned int val; + unsigned int len = msg->len; + int ret; + int i; + + WARN_ON(!mutex_is_locked(&ch7322->mutex)); + + if (len > CH7322_WRBUF_LEN || len < 1) + return -EINVAL; + + ret = regmap_read(ch7322->regmap, CH7322_WRITE, &val); + if (ret) + return ret; + + /* Buffer not ready */ + if (!(val & CH7322_WRITE_MSENT)) + return -EBUSY; + + if (cec_msg_opcode(msg) == -1 && + cec_msg_initiator(msg) == cec_msg_destination(msg)) { + ch7322->tx_flags = CH7322_TX_FLAG_NACK | CH7322_TX_FLAG_RETRY; + } else if (cec_msg_is_broadcast(msg)) { + ch7322->tx_flags = CH7322_TX_FLAG_NACK; + } else { + ch7322->tx_flags = CH7322_TX_FLAG_RETRY; + } + + ret = regmap_write(ch7322->regmap, CH7322_WRITE, len - 1); + if (ret) + return ret; + + for (i = 0; i < len; i++) { + ret = regmap_write(ch7322->regmap, + CH7322_WRBUF + i, msg->msg[i]); + if (ret) + return ret; + } + + return 0; +} + +static int ch7322_receive_message(struct ch7322 *ch7322, struct cec_msg *msg) +{ + unsigned int val; + int ret = 0; + int i; + + WARN_ON(!mutex_is_locked(&ch7322->mutex)); + + ret = regmap_read(ch7322->regmap, CH7322_READ, &val); + if (ret) + return ret; + + /* Message not ready */ + if (!(val & CH7322_READ_NRDT)) + return -EIO; + + msg->len = (val & CH7322_READ_NMASK) + 1; + + /* Read entire RDBUF to clear state */ + for (i = 0; i < CH7322_RDBUF_LEN; i++) { + ret = regmap_read(ch7322->regmap, CH7322_RDBUF + i, &val); + if (ret) + return ret; + msg->msg[i] = (u8)val; + } + + return 0; +} + +static void ch7322_tx_done(struct ch7322 *ch7322) +{ + int ret; + unsigned int val; + u8 status, flags; + + mutex_lock(&ch7322->mutex); + ret = regmap_read(ch7322->regmap, CH7322_WRITE, &val); + flags = ch7322->tx_flags; + mutex_unlock(&ch7322->mutex); + + /* + * The device returns a one-bit OK status which usually means ACK but + * actually means NACK when sending a logical address query or a + * broadcast. + */ + if (ret) + status = CEC_TX_STATUS_ERROR; + else if ((val & CH7322_WRITE_BOK) && (flags & CH7322_TX_FLAG_NACK)) + status = CEC_TX_STATUS_NACK; + else if (val & CH7322_WRITE_BOK) + status = CEC_TX_STATUS_OK; + else if (flags & CH7322_TX_FLAG_NACK) + status = CEC_TX_STATUS_OK; + else + status = CEC_TX_STATUS_NACK; + + if (status == CEC_TX_STATUS_NACK && (flags & CH7322_TX_FLAG_RETRY)) + status |= CEC_TX_STATUS_MAX_RETRIES; + + cec_transmit_attempt_done(ch7322->cec, status); +} + +static void ch7322_rx_done(struct ch7322 *ch7322) +{ + struct cec_msg msg; + int ret; + + mutex_lock(&ch7322->mutex); + ret = ch7322_receive_message(ch7322, &msg); + mutex_unlock(&ch7322->mutex); + + if (ret) + dev_err(&ch7322->i2c->dev, "cec receive error: %d\n", ret); + else + cec_received_msg(ch7322->cec, &msg); +} + +/* + * This device can either monitor the DDC lines to obtain the physical address + * or it can allow the host to program it. This driver lets the device obtain + * it. + */ +static void ch7322_phys_addr(struct ch7322 *ch7322) +{ + unsigned int pah, pal; + int ret = 0; + + mutex_lock(&ch7322->mutex); + ret |= regmap_read(ch7322->regmap, CH7322_PARH, &pah); + ret |= regmap_read(ch7322->regmap, CH7322_PARL, &pal); + mutex_unlock(&ch7322->mutex); + + if (ret) + dev_err(&ch7322->i2c->dev, "phys addr error\n"); + else + cec_s_phys_addr(ch7322->cec, pal | (pah << 8), false); +} + +static irqreturn_t ch7322_irq(int irq, void *dev) +{ + struct ch7322 *ch7322 = dev; + unsigned int data = 0; + + mutex_lock(&ch7322->mutex); + regmap_read(ch7322->regmap, CH7322_INTDATA, &data); + regmap_write(ch7322->regmap, CH7322_INTDATA, data); + mutex_unlock(&ch7322->mutex); + + if (data & CH7322_INTDATA_HPDFALL) + cec_phys_addr_invalidate(ch7322->cec); + + if (data & CH7322_INTDATA_TXMSG) + ch7322_tx_done(ch7322); + + if (data & CH7322_INTDATA_RXMSG) + ch7322_rx_done(ch7322); + + if (data & CH7322_INTDATA_NEWPHA) + ch7322_phys_addr(ch7322); + + if (data & CH7322_INTDATA_ERROR) + dev_dbg(&ch7322->i2c->dev, "unknown error\n"); + + return IRQ_HANDLED; +} + +/* This device is always enabled */ +static int ch7322_cec_adap_enable(struct cec_adapter *adap, bool enable) +{ + return 0; +} + +static int ch7322_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) +{ + struct ch7322 *ch7322 = cec_get_drvdata(adap); + int ret; + + mutex_lock(&ch7322->mutex); + ret = regmap_update_bits(ch7322->regmap, CH7322_ADDLW, + CH7322_ADDLW_MASK, log_addr << 4); + mutex_unlock(&ch7322->mutex); + + return ret; +} + +static int ch7322_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, + u32 signal_free_time, struct cec_msg *msg) +{ + struct ch7322 *ch7322 = cec_get_drvdata(adap); + int ret; + + mutex_lock(&ch7322->mutex); + ret = ch7322_send_message(ch7322, msg); + mutex_unlock(&ch7322->mutex); + + return ret; +} + +static const struct cec_adap_ops ch7322_cec_adap_ops = { + .adap_enable = ch7322_cec_adap_enable, + .adap_log_addr = ch7322_cec_adap_log_addr, + .adap_transmit = ch7322_cec_adap_transmit, +}; + +#if IS_ENABLED(CONFIG_PCI) && IS_ENABLED(CONFIG_DMI) + +struct ch7322_conn_match { + const char *dev_name; + const char *pci_name; + const char *port_name; +}; + +static struct ch7322_conn_match google_endeavour[] = { + { "i2c-PRP0001:00", "0000:00:02.0", "Port B" }, + { "i2c-PRP0001:01", "0000:00:02.0", "Port C" }, + { }, +}; + +static const struct dmi_system_id ch7322_dmi_table[] = { + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Google"), + DMI_MATCH(DMI_BOARD_NAME, "Endeavour"), + }, + .driver_data = google_endeavour, + }, + { }, +}; + +/* Make a best-effort attempt to locate a matching HDMI port */ +static int ch7322_get_port(struct i2c_client *client, + struct device **dev, + const char **port) +{ + const struct dmi_system_id *system; + const struct ch7322_conn_match *conn; + + *dev = NULL; + *port = NULL; + + system = dmi_first_match(ch7322_dmi_table); + if (!system) + return 0; + + for (conn = system->driver_data; conn->dev_name; conn++) { + if (!strcmp(dev_name(&client->dev), conn->dev_name)) { + struct device *d; + + d = bus_find_device_by_name(&pci_bus_type, NULL, + conn->pci_name); + if (!d) + return -EPROBE_DEFER; + + put_device(d); + + *dev = d; + *port = conn->port_name; + + return 0; + } + } + + return 0; +} + +#else + +static int ch7322_get_port(struct i2c_client *client, + struct device **dev, + const char **port) +{ + *dev = NULL; + *port = NULL; + + return 0; +} + +#endif + +static int ch7322_probe(struct i2c_client *client) +{ + struct device *hdmi_dev; + const char *port_name; + struct ch7322 *ch7322; + struct cec_notifier *notifier = NULL; + u32 caps = CEC_CAP_DEFAULTS; + int ret; + unsigned int val; + + ret = ch7322_get_port(client, &hdmi_dev, &port_name); + if (ret) + return ret; + + if (hdmi_dev) + caps |= CEC_CAP_CONNECTOR_INFO; + + ch7322 = devm_kzalloc(&client->dev, sizeof(*ch7322), GFP_KERNEL); + if (!ch7322) + return -ENOMEM; + + ch7322->regmap = devm_regmap_init_i2c(client, &ch7322_regmap); + if (IS_ERR(ch7322->regmap)) + return PTR_ERR(ch7322->regmap); + + ret = regmap_read(ch7322->regmap, CH7322_DID, &val); + if (ret) + return ret; + + if (val != CH7322_DID_CH7322) + return -EOPNOTSUPP; + + mutex_init(&ch7322->mutex); + ch7322->i2c = client; + ch7322->tx_flags = 0; + + i2c_set_clientdata(client, ch7322); + + /* Disable auto mode */ + ret = regmap_write(ch7322->regmap, CH7322_MODE, CH7322_MODE_SW); + if (ret) + goto err_mutex; + + /* Enable logical address register */ + ret = regmap_update_bits(ch7322->regmap, CH7322_CTL, + CH7322_CTL_SPADL, CH7322_CTL_SPADL); + if (ret) + goto err_mutex; + + ch7322->cec = cec_allocate_adapter(&ch7322_cec_adap_ops, ch7322, + dev_name(&client->dev), + caps, 1); + + if (IS_ERR(ch7322->cec)) { + ret = PTR_ERR(ch7322->cec); + goto err_mutex; + } + + ch7322->cec->adap_controls_phys_addr = true; + + if (hdmi_dev) { + notifier = cec_notifier_cec_adap_register(hdmi_dev, + port_name, + ch7322->cec); + if (!notifier) { + ret = -ENOMEM; + goto err_cec; + } + } + + /* Configure, mask, and clear interrupt */ + ret = regmap_write(ch7322->regmap, CH7322_CFG1, 0); + if (ret) + goto err_notifier; + ret = regmap_write(ch7322->regmap, CH7322_INTCTL, CH7322_INTCTL_INTPB); + if (ret) + goto err_notifier; + ret = regmap_write(ch7322->regmap, CH7322_INTDATA, 0xff); + if (ret) + goto err_notifier; + + /* If HPD is up read physical address */ + ret = regmap_read(ch7322->regmap, CH7322_ADDLR, &val); + if (ret) + goto err_notifier; + if (val & CH7322_ADDLR_HPD) + ch7322_phys_addr(ch7322); + + ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, + ch7322_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + client->name, ch7322); + if (ret) + goto err_notifier; + + /* Unmask interrupt */ + mutex_lock(&ch7322->mutex); + ret = regmap_write(ch7322->regmap, CH7322_INTCTL, 0xff); + mutex_unlock(&ch7322->mutex); + + if (ret) + goto err_notifier; + + ret = cec_register_adapter(ch7322->cec, &client->dev); + if (ret) + goto err_notifier; + + dev_info(&client->dev, "device registered\n"); + + return 0; + +err_notifier: + if (notifier) + cec_notifier_cec_adap_unregister(notifier, ch7322->cec); +err_cec: + cec_delete_adapter(ch7322->cec); +err_mutex: + mutex_destroy(&ch7322->mutex); + return ret; +} + +static int ch7322_remove(struct i2c_client *client) +{ + struct ch7322 *ch7322 = i2c_get_clientdata(client); + + /* Mask interrupt */ + mutex_lock(&ch7322->mutex); + regmap_write(ch7322->regmap, CH7322_INTCTL, CH7322_INTCTL_INTPB); + mutex_unlock(&ch7322->mutex); + + cec_unregister_adapter(ch7322->cec); + mutex_destroy(&ch7322->mutex); + + dev_info(&client->dev, "device unregistered\n"); + + return 0; +} + +static const struct of_device_id ch7322_of_match[] = { + { .compatible = "chrontel,ch7322", }, + {}, +}; +MODULE_DEVICE_TABLE(of, ch7322_of_match); + +static struct i2c_driver ch7322_i2c_driver = { + .driver = { + .name = "ch7322", + .of_match_table = of_match_ptr(ch7322_of_match), + }, + .probe_new = ch7322_probe, + .remove = ch7322_remove, +}; + +module_i2c_driver(ch7322_i2c_driver); + +MODULE_DESCRIPTION("Chrontel CH7322 CEC Controller Driver"); +MODULE_AUTHOR("Jeff Chase "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From e1db811e5d94cff7fa51398e36b2b6186ab185a0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 26 Jun 2020 13:53:15 +0200 Subject: media: ov9640: update MODULE_DESCRIPTION Remove the confusing SoC Camera reference. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov9640.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index 482609665305..3a21f51d9325 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -772,6 +772,6 @@ static struct i2c_driver ov9640_i2c_driver = { module_i2c_driver(ov9640_i2c_driver); -MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx"); +MODULE_DESCRIPTION("OmniVision OV96xx CMOS Image Sensor driver"); MODULE_AUTHOR("Marek Vasut "); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 5a55b647395df9c9afabae82550f9e051cc198d8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 26 Jun 2020 13:53:16 +0200 Subject: media: pxa_camera: update MODULE_DESCRIPTION Remove the confusing SoC Camera reference. Signed-off-by: Hans Verkuil Cc: Robert Jarzmik Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/pxa_camera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index 3c5fe737d36f..a7cf830235b6 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -2588,7 +2588,7 @@ static struct platform_driver pxa_camera_driver = { module_platform_driver(pxa_camera_driver); -MODULE_DESCRIPTION("PXA27x SoC Camera Host driver"); +MODULE_DESCRIPTION("PXA27x Camera Driver"); MODULE_AUTHOR("Guennadi Liakhovetski "); MODULE_LICENSE("GPL"); MODULE_VERSION(PXA_CAM_VERSION); -- cgit v1.2.3 From 29af79e17b6b16430f308a6998afc318b5d26888 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 16 Jun 2020 11:45:14 +0200 Subject: media: drx-k: remove unused MulDiv32 function This function has never been used. Found with clang: drivers/media/dvb-frontends/drxk_hard.c:159:19: warning: unused function 'MulDiv32' [-Wunused-function] static inline u32 MulDiv32(u32 a, u32 b, u32 c) Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/drxk_hard.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c index 7e8e5c308d1c..0ae9d8c72d8d 100644 --- a/drivers/media/dvb-frontends/drxk_hard.c +++ b/drivers/media/dvb-frontends/drxk_hard.c @@ -155,17 +155,6 @@ if (debug >= level) \ printk(KERN_DEBUG KBUILD_MODNAME ": %s " fmt, __func__, ##arg); \ } while (0) - -static inline u32 MulDiv32(u32 a, u32 b, u32 c) -{ - u64 tmp64; - - tmp64 = (u64) a * (u64) b; - do_div(tmp64, c); - - return (u32) tmp64; -} - static inline u32 Frac28a(u32 a, u32 c) { int i = 0; -- cgit v1.2.3 From 3fd63734bca51c6b7bb6615fc10eefa620ca58a0 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 16 Jun 2020 11:49:42 +0200 Subject: media: nuvoton-cir: remove setting tx carrier functions Since this driver could never transmit IR, setting the tx carrier makes no sense. Fixes: 87284271b73b8 ("media: rc: nuvoton: remove rudimentary transmit functionality") Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/nuvoton-cir.c | 32 -------------------------------- 1 file changed, 32 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index 48a69bf23236..52d246dc5b3d 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -74,13 +74,6 @@ static inline void nvt_set_reg_bit(struct nvt_dev *nvt, u8 val, u8 reg) nvt_cr_write(nvt, tmp, reg); } -/* clear config register bit without changing other bits */ -static inline void nvt_clear_reg_bit(struct nvt_dev *nvt, u8 val, u8 reg) -{ - u8 tmp = nvt_cr_read(nvt, reg) & ~val; - nvt_cr_write(nvt, tmp, reg); -} - /* enter extended function mode */ static inline int nvt_efm_enable(struct nvt_dev *nvt) { @@ -631,30 +624,6 @@ static u32 nvt_rx_carrier_detect(struct nvt_dev *nvt) return carrier; } #endif -/* - * set carrier frequency - * - * set carrier on 2 registers: CP & CC - * always set CP as 0x81 - * set CC by SPEC, CC = 3MHz/carrier - 1 - */ -static int nvt_set_tx_carrier(struct rc_dev *dev, u32 carrier) -{ - struct nvt_dev *nvt = dev->priv; - u16 val; - - if (carrier == 0) - return -EINVAL; - - nvt_cir_reg_write(nvt, 1, CIR_CP); - val = 3000000 / (carrier) - 1; - nvt_cir_reg_write(nvt, val & 0xff, CIR_CC); - - nvt_dbg("cp: 0x%x cc: 0x%x\n", - nvt_cir_reg_read(nvt, CIR_CP), nvt_cir_reg_read(nvt, CIR_CC)); - - return 0; -} static int nvt_ir_raw_set_wakeup_filter(struct rc_dev *dev, struct rc_scancode_filter *sc_filter) @@ -1022,7 +991,6 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) rdev->encode_wakeup = true; rdev->open = nvt_open; rdev->close = nvt_close; - rdev->s_tx_carrier = nvt_set_tx_carrier; rdev->s_wakeup_filter = nvt_ir_raw_set_wakeup_filter; rdev->device_name = "Nuvoton w836x7hg Infrared Remote Transceiver"; rdev->input_phys = "nuvoton/cir0"; -- cgit v1.2.3 From 0390430deffbeb05bf08dfc8168d53f6e693b768 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 16 Jun 2020 11:54:06 +0200 Subject: media: fintek-cir: remove unused function fintek_clear_reg_bit Found with clang: drivers/media/rc/fintek-cir.c:55:20: warning: unused function 'fintek_clear_reg_bit' [-Wunused-function] static inline void fintek_clear_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg) Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/fintek-cir.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c index b74bb13161fd..8e3177c5b586 100644 --- a/drivers/media/rc/fintek-cir.c +++ b/drivers/media/rc/fintek-cir.c @@ -51,13 +51,6 @@ static inline void fintek_set_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg) fintek_cr_write(fintek, tmp, reg); } -/* clear config register bit without changing other bits */ -static inline void fintek_clear_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg) -{ - u8 tmp = fintek_cr_read(fintek, reg) & ~val; - fintek_cr_write(fintek, tmp, reg); -} - /* enter config mode */ static inline void fintek_config_mode_enable(struct fintek_dev *fintek) { -- cgit v1.2.3 From 39c2cf7544d63882709236ca2fb801db7cc2bc52 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 16 Jun 2020 12:04:17 +0200 Subject: media: cx23888: remove unused functions Found with clang: drivers/media/pci/cx23885/cx23888-ir.c:178:19: warning: unused function 'ns_to_clock_divider' [-Wunused-function] static inline u16 ns_to_clock_divider(unsigned int ns) ^ drivers/media/pci/cx23885/cx23888-ir.c:184:28: warning: unused function 'clock_divider_to_ns' [-Wunused-function] static inline unsigned int clock_divider_to_ns(unsigned int divider) ^ drivers/media/pci/cx23885/cx23888-ir.c:202:19: warning: unused function 'freq_to_clock_divider' [-Wunused-function] static inline u16 freq_to_clock_divider(unsigned int freq, ^ Cc: Andy Walls Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx23885/cx23888-ir.c | 20 -------------------- 1 file changed, 20 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/cx23885/cx23888-ir.c b/drivers/media/pci/cx23885/cx23888-ir.c index d59ca3601785..ad7f8ccad526 100644 --- a/drivers/media/pci/cx23885/cx23888-ir.c +++ b/drivers/media/pci/cx23885/cx23888-ir.c @@ -175,19 +175,6 @@ static inline u16 count_to_clock_divider(unsigned int d) return (u16) d; } -static inline u16 ns_to_clock_divider(unsigned int ns) -{ - return count_to_clock_divider( - DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ / 1000000 * ns, 1000)); -} - -static inline unsigned int clock_divider_to_ns(unsigned int divider) -{ - /* Period of the Rx or Tx clock in ns */ - return DIV_ROUND_CLOSEST((divider + 1) * 1000, - CX23888_IR_REFCLK_FREQ / 1000000); -} - static inline u16 carrier_freq_to_clock_divider(unsigned int freq) { return count_to_clock_divider( @@ -199,13 +186,6 @@ static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider) return DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, (divider + 1) * 16); } -static inline u16 freq_to_clock_divider(unsigned int freq, - unsigned int rollovers) -{ - return count_to_clock_divider( - DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, freq * rollovers)); -} - static inline unsigned int clock_divider_to_freq(unsigned int divider, unsigned int rollovers) { -- cgit v1.2.3 From 3b4cfc6966ecaefdf46a1369923a55e7bdc86d19 Mon Sep 17 00:00:00 2001 From: Evgeny Novikov Date: Thu, 18 Jun 2020 15:15:38 +0200 Subject: media: rc: return proper error code on error handling path in init If lirc_dev_init() fails during module initialization, rc_core_init() returns 0 denoting success. This can cause different issues during further operation of the module. The patch fixes the return value of rc_core_init() on the corresponding error handling path. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Evgeny Novikov Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/rc-main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index d7064d664d52..7b53066d9d07 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -2052,7 +2052,7 @@ static int __init rc_core_init(void) if (rc) { pr_err("rc_core: unable to init lirc\n"); class_unregister(&rc_class); - return 0; + return rc; } led_trigger_register_simple("rc-feedback", &led_feedback); -- cgit v1.2.3 From e0eb34810113dbbf1ace57440cf48d514312a373 Mon Sep 17 00:00:00 2001 From: Mansur Alisha Shaik Date: Fri, 1 May 2020 08:28:00 +0200 Subject: media: venus: fix multiple encoder crash Currently we are considering the instances which are available in core->inst list for load calculation in min_loaded_core() function, but this is incorrect because by the time we call decide_core() for second instance, the third instance not filled yet codec_freq_data pointer. Solve this by considering the instances whose session has started. Cc: stable@vger.kernel.org # v5.7+ Fixes: 4ebf969375bc ("media: venus: introduce core selection") Tested-by: Douglas Anderson Signed-off-by: Mansur Alisha Shaik Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/pm_helpers.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index abf93158857b..531e7a41658f 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -496,6 +496,10 @@ min_loaded_core(struct venus_inst *inst, u32 *min_coreid, u32 *min_load) list_for_each_entry(inst_pos, &core->instances, list) { if (inst_pos == inst) continue; + + if (inst_pos->state != INST_START) + continue; + vpp_freq = inst_pos->clk_data.codec_freq_data->vpp_freq; coreid = inst_pos->clk_data.core_id; -- cgit v1.2.3 From 831b8755fd2f60cf00cbf854c2a2eccb03f2060b Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Sun, 24 May 2020 06:31:32 +0200 Subject: media: venus: venc: Fix runtime PM imbalance in venc_open pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Signed-off-by: Dinghao Liu Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/venc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index feed648550d1..513bbc07f7bc 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -1177,7 +1177,7 @@ static int venc_open(struct file *file) ret = pm_runtime_get_sync(core->dev_enc); if (ret < 0) - goto err_free_inst; + goto err_put_sync; ret = venc_ctrl_init(inst); if (ret) @@ -1222,7 +1222,6 @@ err_ctrl_deinit: venc_ctrl_deinit(inst); err_put_sync: pm_runtime_put_sync(core->dev_enc); -err_free_inst: kfree(inst); return ret; } -- cgit v1.2.3 From fd22e8eb4145852223b36b0e6c7e308ed5905ad2 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 27 May 2020 14:16:48 +0200 Subject: media: rcar-vin: Invalidate pipeline if conversion is not possible on input formats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up until now the VIN was capable to convert any of its supported input mbus formats to any of it's supported output pixel formats. With the addition of RAW formats this is no longer true. This patch invalidates the pipeline by adding a check if given vin input format can be converted to supported output pixel format. Signed-off-by: Lad Prabhakar Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-dma.c | 6 +++++- drivers/media/platform/rcar-vin/rcar-v4l2.c | 11 ++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index 1a30cd036371..2b26204910fd 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -1110,11 +1110,15 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd, case MEDIA_BUS_FMT_UYVY8_2X8: case MEDIA_BUS_FMT_UYVY10_2X10: case MEDIA_BUS_FMT_RGB888_1X24: - vin->mbus_code = fmt.format.code; + break; + case MEDIA_BUS_FMT_SRGGB8_1X8: + if (vin->format.pixelformat != V4L2_PIX_FMT_SRGGB8) + return -EPIPE; break; default: return -EPIPE; } + vin->mbus_code = fmt.format.code; switch (fmt.format.field) { case V4L2_FIELD_TOP: diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index f421e2584875..d3b6a992b4a2 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -350,9 +350,9 @@ static int rvin_enum_fmt_vid_cap(struct file *file, void *priv, * all of the related pixel formats. If mbus_code is not set enumerate * all possible pixelformats. * - * TODO: Once raw capture formats are added to the driver this needs - * to be extended so raw media bus codes only result in raw pixel - * formats. + * TODO: Once raw MEDIA_BUS_FMT_SRGGB12_1X12 format is added to the + * driver this needs to be extended so raw media bus code only result in + * raw pixel format. */ switch (f->mbus_code) { case 0: @@ -362,6 +362,11 @@ static int rvin_enum_fmt_vid_cap(struct file *file, void *priv, case MEDIA_BUS_FMT_UYVY10_2X10: case MEDIA_BUS_FMT_RGB888_1X24: break; + case MEDIA_BUS_FMT_SRGGB8_1X8: + if (f->index) + return -EINVAL; + f->pixelformat = V4L2_PIX_FMT_SRGGB8; + return 0; default: return -EINVAL; } -- cgit v1.2.3 From e87c1a81f158d6fc7b3346eb88c2d76a044f837d Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 27 May 2020 14:16:49 +0200 Subject: media: rcar-vin: Add support for MEDIA_BUS_FMT_SRGGB8_1X8 format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for MEDIA_BUS_FMT_SRGGB8_1X8 format in rcar-vin by setting format type to RAW8 in VNMC register and appropriately setting the bpp and bytesperline to enable V4L2_PIX_FMT_SRGGB8. Signed-off-by: Lad Prabhakar Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-dma.c | 15 ++++++++++++++- drivers/media/platform/rcar-vin/rcar-v4l2.c | 4 ++++ 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index 2b26204910fd..a5dbb90c5210 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -85,6 +85,7 @@ #define VNMC_INF_YUV8_BT601 (1 << 16) #define VNMC_INF_YUV10_BT656 (2 << 16) #define VNMC_INF_YUV10_BT601 (3 << 16) +#define VNMC_INF_RAW8 (4 << 16) #define VNMC_INF_YUV16 (5 << 16) #define VNMC_INF_RGB888 (6 << 16) #define VNMC_VUP (1 << 10) @@ -587,13 +588,19 @@ void rvin_crop_scale_comp(struct rvin_dev *vin) rvin_write(vin, vin->crop.top, VNSLPRC_REG); rvin_write(vin, vin->crop.top + vin->crop.height - 1, VNELPRC_REG); - /* TODO: Add support for the UDS scaler. */ if (vin->info->model != RCAR_GEN3) rvin_crop_scale_comp_gen2(vin); fmt = rvin_format_from_pixel(vin, vin->format.pixelformat); stride = vin->format.bytesperline / fmt->bpp; + + /* For RAW8 format bpp is 1, but the hardware process RAW8 + * format in 2 pixel unit hence configure VNIS_REG as stride / 2. + */ + if (vin->format.pixelformat == V4L2_PIX_FMT_SRGGB8) + stride /= 2; + rvin_write(vin, stride, VNIS_REG); } @@ -676,6 +683,9 @@ static int rvin_setup(struct rvin_dev *vin) input_is_yuv = true; break; + case MEDIA_BUS_FMT_SRGGB8_1X8: + vnmc |= VNMC_INF_RAW8; + break; default: break; } @@ -737,6 +747,9 @@ static int rvin_setup(struct rvin_dev *vin) case V4L2_PIX_FMT_ABGR32: dmr = VNDMR_A8BIT(vin->alpha) | VNDMR_EXRGB | VNDMR_DTMD_ARGB; break; + case V4L2_PIX_FMT_SRGGB8: + dmr = 0; + break; default: vin_err(vin, "Invalid pixelformat (0x%x)\n", vin->format.pixelformat); diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index d3b6a992b4a2..0e066bba747e 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -66,6 +66,10 @@ static const struct rvin_video_format rvin_formats[] = { .fourcc = V4L2_PIX_FMT_ABGR32, .bpp = 4, }, + { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .bpp = 1, + }, }; const struct rvin_video_format *rvin_format_from_pixel(struct rvin_dev *vin, -- cgit v1.2.3 From 675616554d0a5caff138008ee9bd4623bc4390b2 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 27 May 2020 14:16:50 +0200 Subject: media: rcar-csi2: Add support for MEDIA_BUS_FMT_SRGGB8_1X8 format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds support for MEDIA_BUS_FMT_SRGGB8_1X8 format for CSI2 input. Signed-off-by: Lad Prabhakar Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-csi2.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c index 151e6a90c5fb..45fdf4e74e51 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -320,6 +320,7 @@ static const struct rcar_csi2_format rcar_csi2_formats[] = { { .code = MEDIA_BUS_FMT_YUYV8_1X16, .datatype = 0x1e, .bpp = 16 }, { .code = MEDIA_BUS_FMT_UYVY8_2X8, .datatype = 0x1e, .bpp = 16 }, { .code = MEDIA_BUS_FMT_YUYV10_2X10, .datatype = 0x1e, .bpp = 20 }, + { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .datatype = 0x2a, .bpp = 8 }, }; static const struct rcar_csi2_format *rcsi2_code_to_fmt(unsigned int code) -- cgit v1.2.3 From 030b161c4dc8a4d9a3dcecd7101680e3f871748d Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Thu, 28 May 2020 15:26:05 +0200 Subject: media: use v4l2_rect_enclosed helper Several drivers implement the same enclosed_rectangle() function to check if a rectangle is enclosed into another. Replace this with the newly added v4l2_rect_enclosed() helper function. Signed-off-by: Benoit Parrot Acked-by: Andrzej Pietrasiewicz Reviewed-by: Lad Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/am437x/am437x-vpfe.c | 19 +++---------------- drivers/media/platform/exynos4-is/fimc-capture.c | 18 +++--------------- drivers/media/platform/exynos4-is/fimc-lite.c | 18 +++--------------- drivers/media/platform/s5p-jpeg/jpeg-core.c | 16 ++-------------- 4 files changed, 11 insertions(+), 60 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index 66079cc41f38..0fb9f9ba1219 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "am437x-vpfe.h" @@ -1987,20 +1988,6 @@ vpfe_g_selection(struct file *file, void *fh, struct v4l2_selection *s) return 0; } -static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) -{ - if (a->left < b->left || a->top < b->top) - return 0; - - if (a->left + a->width > b->left + b->width) - return 0; - - if (a->top + a->height > b->top + b->height) - return 0; - - return 1; -} - static int vpfe_s_selection(struct file *file, void *fh, struct v4l2_selection *s) { @@ -2025,10 +2012,10 @@ vpfe_s_selection(struct file *file, void *fh, struct v4l2_selection *s) r.left = clamp_t(unsigned int, r.left, 0, cr.width - r.width); r.top = clamp_t(unsigned int, r.top, 0, cr.height - r.height); - if (s->flags & V4L2_SEL_FLAG_LE && !enclosed_rectangle(&r, &s->r)) + if (s->flags & V4L2_SEL_FLAG_LE && !v4l2_rect_enclosed(&r, &s->r)) return -ERANGE; - if (s->flags & V4L2_SEL_FLAG_GE && !enclosed_rectangle(&s->r, &r)) + if (s->flags & V4L2_SEL_FLAG_GE && !v4l2_rect_enclosed(&s->r, &r)) return -ERANGE; s->r = vpfe->crop = r; diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index dd33b888971f..e7a4b06e6dfe 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -1301,19 +1302,6 @@ static int fimc_cap_g_selection(struct file *file, void *fh, return -EINVAL; } -/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ -static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) -{ - if (a->left < b->left || a->top < b->top) - return 0; - if (a->left + a->width > b->left + b->width) - return 0; - if (a->top + a->height > b->top + b->height) - return 0; - - return 1; -} - static int fimc_cap_s_selection(struct file *file, void *fh, struct v4l2_selection *s) { @@ -1336,11 +1324,11 @@ static int fimc_cap_s_selection(struct file *file, void *fh, fimc_capture_try_selection(ctx, &rect, s->target); if (s->flags & V4L2_SEL_FLAG_LE && - !enclosed_rectangle(&rect, &s->r)) + !v4l2_rect_enclosed(&rect, &s->r)) return -ERANGE; if (s->flags & V4L2_SEL_FLAG_GE && - !enclosed_rectangle(&s->r, &rect)) + !v4l2_rect_enclosed(&s->r, &rect)) return -ERANGE; s->r = rect; diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index 394e0818f2d5..9c666f663ab4 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -868,19 +869,6 @@ static int fimc_lite_reqbufs(struct file *file, void *priv, return ret; } -/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ -static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) -{ - if (a->left < b->left || a->top < b->top) - return 0; - if (a->left + a->width > b->left + b->width) - return 0; - if (a->top + a->height > b->top + b->height) - return 0; - - return 1; -} - static int fimc_lite_g_selection(struct file *file, void *fh, struct v4l2_selection *sel) { @@ -922,11 +910,11 @@ static int fimc_lite_s_selection(struct file *file, void *fh, fimc_lite_try_compose(fimc, &rect); if ((sel->flags & V4L2_SEL_FLAG_LE) && - !enclosed_rectangle(&rect, &sel->r)) + !v4l2_rect_enclosed(&rect, &sel->r)) return -ERANGE; if ((sel->flags & V4L2_SEL_FLAG_GE) && - !enclosed_rectangle(&sel->r, &rect)) + !v4l2_rect_enclosed(&sel->r, &rect)) return -ERANGE; sel->r = rect; diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 86bda3947110..9b22dd8e34f4 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -1735,19 +1736,6 @@ static int exynos3250_jpeg_try_downscale(struct s5p_jpeg_ctx *ctx, return 0; } -/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ -static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) -{ - if (a->left < b->left || a->top < b->top) - return 0; - if (a->left + a->width > b->left + b->width) - return 0; - if (a->top + a->height > b->top + b->height) - return 0; - - return 1; -} - static int exynos3250_jpeg_try_crop(struct s5p_jpeg_ctx *ctx, struct v4l2_rect *r) { @@ -1780,7 +1768,7 @@ static int exynos3250_jpeg_try_crop(struct s5p_jpeg_ctx *ctx, r->left = round_down(r->left, 2); r->top = round_down(r->top, 2); - if (!enclosed_rectangle(r, &base_rect)) + if (!v4l2_rect_enclosed(r, &base_rect)) return -EINVAL; ctx->crop_rect.left = r->left; -- cgit v1.2.3 From b820935b35479e823eb6ce133cdbe371dced7e95 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 21 Jun 2020 02:23:43 +0200 Subject: media: vb2: Print the queue pointer in debug messages When debugging issues that involve more than one video queue, messages related to multiple queues get interleaved without any easy way to tell which queue they relate to. Fix this by adding a queue name to vb2_queue, and printing it in all debug messages in the vb2 core and V4L2 layers. If the name isn't set by drivers, it is automatically filled with the queue direction and address. Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 226 ++++++++++++------------ drivers/media/common/videobuf2/videobuf2-v4l2.c | 68 ++++--- include/media/videobuf2-core.h | 4 + include/media/videobuf2-v4l2.h | 13 ++ 4 files changed, 174 insertions(+), 137 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 37d0186ba330..abaf28e057eb 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -34,10 +34,11 @@ static int debug; module_param(debug, int, 0644); -#define dprintk(level, fmt, arg...) \ - do { \ - if (debug >= level) \ - pr_info("%s: " fmt, __func__, ## arg); \ +#define dprintk(q, level, fmt, arg...) \ + do { \ + if (debug >= level) \ + pr_info("[%s] %s: " fmt, (q)->name, __func__, \ + ## arg); \ } while (0) #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -51,8 +52,8 @@ module_param(debug, int, 0644); */ #define log_memop(vb, op) \ - dprintk(2, "call_memop(%p, %d, %s)%s\n", \ - (vb)->vb2_queue, (vb)->index, #op, \ + dprintk((vb)->vb2_queue, 2, "call_memop(%d, %s)%s\n", \ + (vb)->index, #op, \ (vb)->vb2_queue->mem_ops->op ? "" : " (nop)") #define call_memop(vb, op, args...) \ @@ -90,7 +91,7 @@ module_param(debug, int, 0644); }) #define log_qop(q, op) \ - dprintk(2, "call_qop(%p, %s)%s\n", q, #op, \ + dprintk(q, 2, "call_qop(%s)%s\n", #op, \ (q)->ops->op ? "" : " (nop)") #define call_qop(q, op, args...) \ @@ -113,8 +114,8 @@ module_param(debug, int, 0644); }) #define log_vb_qop(vb, op, args...) \ - dprintk(2, "call_vb_qop(%p, %d, %s)%s\n", \ - (vb)->vb2_queue, (vb)->index, #op, \ + dprintk((vb)->vb2_queue, 2, "call_vb_qop(%d, %s)%s\n", \ + (vb)->index, #op, \ (vb)->vb2_queue->ops->op ? "" : " (nop)") #define call_vb_qop(vb, op, args...) \ @@ -246,7 +247,8 @@ static void __vb2_buf_mem_free(struct vb2_buffer *vb) for (plane = 0; plane < vb->num_planes; ++plane) { call_void_memop(vb, put, vb->planes[plane].mem_priv); vb->planes[plane].mem_priv = NULL; - dprintk(3, "freed plane %d of buffer %d\n", plane, vb->index); + dprintk(vb->vb2_queue, 3, "freed plane %d of buffer %d\n", + plane, vb->index); } } @@ -354,7 +356,7 @@ static void __setup_offsets(struct vb2_buffer *vb) for (plane = 0; plane < vb->num_planes; ++plane) { vb->planes[plane].m.offset = off; - dprintk(3, "buffer %d, plane %d offset 0x%08lx\n", + dprintk(q, 3, "buffer %d, plane %d offset 0x%08lx\n", vb->index, plane, off); off += vb->planes[plane].length; @@ -385,7 +387,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, /* Allocate videobuf buffer structures */ vb = kzalloc(q->buf_struct_size, GFP_KERNEL); if (!vb) { - dprintk(1, "memory alloc for buffer struct failed\n"); + dprintk(q, 1, "memory alloc for buffer struct failed\n"); break; } @@ -407,7 +409,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, if (memory == VB2_MEMORY_MMAP) { ret = __vb2_buf_mem_alloc(vb); if (ret) { - dprintk(1, "failed allocating memory for buffer %d\n", + dprintk(q, 1, "failed allocating memory for buffer %d\n", buffer); q->bufs[vb->index] = NULL; kfree(vb); @@ -421,7 +423,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, */ ret = call_vb_qop(vb, buf_init, vb); if (ret) { - dprintk(1, "buffer %d %p initialization failed\n", + dprintk(q, 1, "buffer %d %p initialization failed\n", buffer, vb); __vb2_buf_mem_free(vb); q->bufs[vb->index] = NULL; @@ -431,7 +433,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, } } - dprintk(3, "allocated %d buffers, %d plane(s) each\n", + dprintk(q, 3, "allocated %d buffers, %d plane(s) each\n", buffer, num_planes); return buffer; @@ -483,7 +485,7 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) if (q->bufs[buffer] == NULL) continue; if (q->bufs[buffer]->state == VB2_BUF_STATE_PREPARING) { - dprintk(1, "preparing buffers, cannot free\n"); + dprintk(q, 1, "preparing buffers, cannot free\n"); return -EAGAIN; } } @@ -661,12 +663,12 @@ int vb2_verify_memory_type(struct vb2_queue *q, { if (memory != VB2_MEMORY_MMAP && memory != VB2_MEMORY_USERPTR && memory != VB2_MEMORY_DMABUF) { - dprintk(1, "unsupported memory type\n"); + dprintk(q, 1, "unsupported memory type\n"); return -EINVAL; } if (type != q->type) { - dprintk(1, "requested type is incorrect\n"); + dprintk(q, 1, "requested type is incorrect\n"); return -EINVAL; } @@ -675,17 +677,17 @@ int vb2_verify_memory_type(struct vb2_queue *q, * are available. */ if (memory == VB2_MEMORY_MMAP && __verify_mmap_ops(q)) { - dprintk(1, "MMAP for current setup unsupported\n"); + dprintk(q, 1, "MMAP for current setup unsupported\n"); return -EINVAL; } if (memory == VB2_MEMORY_USERPTR && __verify_userptr_ops(q)) { - dprintk(1, "USERPTR for current setup unsupported\n"); + dprintk(q, 1, "USERPTR for current setup unsupported\n"); return -EINVAL; } if (memory == VB2_MEMORY_DMABUF && __verify_dmabuf_ops(q)) { - dprintk(1, "DMABUF for current setup unsupported\n"); + dprintk(q, 1, "DMABUF for current setup unsupported\n"); return -EINVAL; } @@ -695,7 +697,7 @@ int vb2_verify_memory_type(struct vb2_queue *q, * do the memory and type validation. */ if (vb2_fileio_is_active(q)) { - dprintk(1, "file io in progress\n"); + dprintk(q, 1, "file io in progress\n"); return -EBUSY; } return 0; @@ -717,7 +719,7 @@ static bool verify_consistency_attr(struct vb2_queue *q, bool consistent_mem) bool queue_is_consistent = !(q->dma_attrs & DMA_ATTR_NON_CONSISTENT); if (consistent_mem != queue_is_consistent) { - dprintk(1, "memory consistency model mismatch\n"); + dprintk(q, 1, "memory consistency model mismatch\n"); return false; } return true; @@ -736,12 +738,12 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, consistent_mem = false; if (q->streaming) { - dprintk(1, "streaming active\n"); + dprintk(q, 1, "streaming active\n"); return -EBUSY; } if (q->waiting_in_dqbuf && *count) { - dprintk(1, "another dup()ped fd is waiting for a buffer\n"); + dprintk(q, 1, "another dup()ped fd is waiting for a buffer\n"); return -EBUSY; } @@ -755,7 +757,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, mutex_lock(&q->mmap_lock); if (debug && q->memory == VB2_MEMORY_MMAP && __buffers_in_use(q)) - dprintk(1, "memory in use, orphaning buffers\n"); + dprintk(q, 1, "memory in use, orphaning buffers\n"); /* * Call queue_cancel to clean up any buffers in the @@ -807,7 +809,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, allocated_buffers = __vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes); if (allocated_buffers == 0) { - dprintk(1, "memory allocation failed\n"); + dprintk(q, 1, "memory allocation failed\n"); return -ENOMEM; } @@ -882,13 +884,13 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, consistent_mem = false; if (q->num_buffers == VB2_MAX_FRAME) { - dprintk(1, "maximum number of buffers already allocated\n"); + dprintk(q, 1, "maximum number of buffers already allocated\n"); return -ENOBUFS; } if (!q->num_buffers) { if (q->waiting_in_dqbuf && *count) { - dprintk(1, "another dup()ped fd is waiting for a buffer\n"); + dprintk(q, 1, "another dup()ped fd is waiting for a buffer\n"); return -EBUSY; } memset(q->alloc_devs, 0, sizeof(q->alloc_devs)); @@ -897,7 +899,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, q->waiting_for_buffers = !q->is_output; } else { if (q->memory != memory) { - dprintk(1, "memory model mismatch\n"); + dprintk(q, 1, "memory model mismatch\n"); return -EINVAL; } if (!verify_consistency_attr(q, consistent_mem)) @@ -924,7 +926,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, allocated_buffers = __vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes); if (allocated_buffers == 0) { - dprintk(1, "memory allocation failed\n"); + dprintk(q, 1, "memory allocation failed\n"); return -ENOMEM; } @@ -1013,7 +1015,7 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) */ vb->cnt_buf_done++; #endif - dprintk(4, "done processing on buffer %d, state: %d\n", + dprintk(q, 4, "done processing on buffer %d, state: %d\n", vb->index, state); if (state != VB2_BUF_STATE_QUEUED) @@ -1099,12 +1101,12 @@ static int __prepare_userptr(struct vb2_buffer *vb) && vb->planes[plane].length == planes[plane].length) continue; - dprintk(3, "userspace address for plane %d changed, reacquiring memory\n", + dprintk(q, 3, "userspace address for plane %d changed, reacquiring memory\n", plane); /* Check if the provided plane buffer is large enough */ if (planes[plane].length < vb->planes[plane].min_length) { - dprintk(1, "provided buffer size %u is less than setup size %u for plane %d\n", + dprintk(q, 1, "provided buffer size %u is less than setup size %u for plane %d\n", planes[plane].length, vb->planes[plane].min_length, plane); @@ -1134,7 +1136,7 @@ static int __prepare_userptr(struct vb2_buffer *vb) planes[plane].m.userptr, planes[plane].length, q->dma_dir); if (IS_ERR(mem_priv)) { - dprintk(1, "failed acquiring userspace memory for plane %d\n", + dprintk(q, 1, "failed acquiring userspace memory for plane %d\n", plane); ret = PTR_ERR(mem_priv); goto err; @@ -1161,14 +1163,14 @@ static int __prepare_userptr(struct vb2_buffer *vb) */ ret = call_vb_qop(vb, buf_init, vb); if (ret) { - dprintk(1, "buffer initialization failed\n"); + dprintk(q, 1, "buffer initialization failed\n"); goto err; } } ret = call_vb_qop(vb, buf_prepare, vb); if (ret) { - dprintk(1, "buffer preparation failed\n"); + dprintk(q, 1, "buffer preparation failed\n"); call_void_vb_qop(vb, buf_cleanup, vb); goto err; } @@ -1211,7 +1213,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) struct dma_buf *dbuf = dma_buf_get(planes[plane].m.fd); if (IS_ERR_OR_NULL(dbuf)) { - dprintk(1, "invalid dmabuf fd for plane %d\n", + dprintk(q, 1, "invalid dmabuf fd for plane %d\n", plane); ret = -EINVAL; goto err; @@ -1222,7 +1224,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) planes[plane].length = dbuf->size; if (planes[plane].length < vb->planes[plane].min_length) { - dprintk(1, "invalid dmabuf length %u for plane %d, minimum length %u\n", + dprintk(q, 1, "invalid dmabuf length %u for plane %d, minimum length %u\n", planes[plane].length, plane, vb->planes[plane].min_length); dma_buf_put(dbuf); @@ -1237,7 +1239,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) continue; } - dprintk(3, "buffer for plane %d changed\n", plane); + dprintk(q, 3, "buffer for plane %d changed\n", plane); if (!reacquired) { reacquired = true; @@ -1257,7 +1259,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) q->alloc_devs[plane] ? : q->dev, dbuf, planes[plane].length, q->dma_dir); if (IS_ERR(mem_priv)) { - dprintk(1, "failed to attach dmabuf\n"); + dprintk(q, 1, "failed to attach dmabuf\n"); ret = PTR_ERR(mem_priv); dma_buf_put(dbuf); goto err; @@ -1278,7 +1280,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) ret = call_memop(vb, map_dmabuf, vb->planes[plane].mem_priv); if (ret) { - dprintk(1, "failed to map dmabuf for plane %d\n", + dprintk(q, 1, "failed to map dmabuf for plane %d\n", plane); goto err; } @@ -1303,14 +1305,14 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) */ ret = call_vb_qop(vb, buf_init, vb); if (ret) { - dprintk(1, "buffer initialization failed\n"); + dprintk(q, 1, "buffer initialization failed\n"); goto err; } } ret = call_vb_qop(vb, buf_prepare, vb); if (ret) { - dprintk(1, "buffer preparation failed\n"); + dprintk(q, 1, "buffer preparation failed\n"); call_void_vb_qop(vb, buf_cleanup, vb); goto err; } @@ -1345,7 +1347,7 @@ static int __buf_prepare(struct vb2_buffer *vb) int ret; if (q->error) { - dprintk(1, "fatal error occurred on queue\n"); + dprintk(q, 1, "fatal error occurred on queue\n"); return -EIO; } @@ -1356,7 +1358,7 @@ static int __buf_prepare(struct vb2_buffer *vb) if (q->is_output) { ret = call_vb_qop(vb, buf_out_validate, vb); if (ret) { - dprintk(1, "buffer validation failed\n"); + dprintk(q, 1, "buffer validation failed\n"); return ret; } } @@ -1380,7 +1382,7 @@ static int __buf_prepare(struct vb2_buffer *vb) } if (ret) { - dprintk(1, "buffer preparation failed: %d\n", ret); + dprintk(q, 1, "buffer preparation failed: %d\n", ret); vb->state = orig_state; return ret; } @@ -1488,12 +1490,12 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb) vb = q->bufs[index]; if (vb->state != VB2_BUF_STATE_DEQUEUED) { - dprintk(1, "invalid buffer state %d\n", + dprintk(q, 1, "invalid buffer state %d\n", vb->state); return -EINVAL; } if (vb->prepared) { - dprintk(1, "buffer already prepared\n"); + dprintk(q, 1, "buffer already prepared\n"); return -EINVAL; } @@ -1504,7 +1506,7 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb) /* Fill buffer information for the userspace */ call_void_bufop(q, fill_user_buffer, vb, pb); - dprintk(2, "prepare of buffer %d succeeded\n", vb->index); + dprintk(q, 2, "prepare of buffer %d succeeded\n", vb->index); return 0; } @@ -1542,7 +1544,7 @@ static int vb2_start_streaming(struct vb2_queue *q) q->start_streaming_called = 0; - dprintk(1, "driver refused to start streaming\n"); + dprintk(q, 1, "driver refused to start streaming\n"); /* * If you see this warning, then the driver isn't cleaning up properly * after a failed start_streaming(). See the start_streaming() @@ -1580,7 +1582,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, int ret; if (q->error) { - dprintk(1, "fatal error occurred on queue\n"); + dprintk(q, 1, "fatal error occurred on queue\n"); return -EIO; } @@ -1588,14 +1590,14 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, if (!req && vb->state != VB2_BUF_STATE_IN_REQUEST && q->requires_requests) { - dprintk(1, "qbuf requires a request\n"); + dprintk(q, 1, "qbuf requires a request\n"); return -EBADR; } if ((req && q->uses_qbuf) || (!req && vb->state != VB2_BUF_STATE_IN_REQUEST && q->uses_requests)) { - dprintk(1, "queue in wrong mode (qbuf vs requests)\n"); + dprintk(q, 1, "queue in wrong mode (qbuf vs requests)\n"); return -EBUSY; } @@ -1604,7 +1606,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, q->uses_requests = 1; if (vb->state != VB2_BUF_STATE_DEQUEUED) { - dprintk(1, "buffer %d not in dequeued state\n", + dprintk(q, 1, "buffer %d not in dequeued state\n", vb->index); return -EINVAL; } @@ -1612,7 +1614,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, if (q->is_output && !vb->prepared) { ret = call_vb_qop(vb, buf_out_validate, vb); if (ret) { - dprintk(1, "buffer validation failed\n"); + dprintk(q, 1, "buffer validation failed\n"); return ret; } } @@ -1648,7 +1650,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, call_void_bufop(q, fill_user_buffer, vb, pb); } - dprintk(2, "qbuf of buffer %d succeeded\n", vb->index); + dprintk(q, 2, "qbuf of buffer %d succeeded\n", vb->index); return 0; } @@ -1665,10 +1667,10 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, } break; case VB2_BUF_STATE_PREPARING: - dprintk(1, "buffer still being prepared\n"); + dprintk(q, 1, "buffer still being prepared\n"); return -EINVAL; default: - dprintk(1, "invalid buffer state %d\n", vb->state); + dprintk(q, 1, "invalid buffer state %d\n", vb->state); return -EINVAL; } @@ -1710,7 +1712,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, return ret; } - dprintk(2, "qbuf of buffer %d succeeded\n", vb->index); + dprintk(q, 2, "qbuf of buffer %d succeeded\n", vb->index); return 0; } EXPORT_SYMBOL_GPL(vb2_core_qbuf); @@ -1736,22 +1738,22 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) int ret; if (q->waiting_in_dqbuf) { - dprintk(1, "another dup()ped fd is waiting for a buffer\n"); + dprintk(q, 1, "another dup()ped fd is waiting for a buffer\n"); return -EBUSY; } if (!q->streaming) { - dprintk(1, "streaming off, will not wait for buffers\n"); + dprintk(q, 1, "streaming off, will not wait for buffers\n"); return -EINVAL; } if (q->error) { - dprintk(1, "Queue in error state, will not wait for buffers\n"); + dprintk(q, 1, "Queue in error state, will not wait for buffers\n"); return -EIO; } if (q->last_buffer_dequeued) { - dprintk(3, "last buffer dequeued already, will not wait for buffers\n"); + dprintk(q, 3, "last buffer dequeued already, will not wait for buffers\n"); return -EPIPE; } @@ -1763,7 +1765,7 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) } if (nonblocking) { - dprintk(3, "nonblocking and no buffers to dequeue, will not wait\n"); + dprintk(q, 3, "nonblocking and no buffers to dequeue, will not wait\n"); return -EAGAIN; } @@ -1778,7 +1780,7 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) /* * All locks have been released, it is safe to sleep now. */ - dprintk(3, "will sleep waiting for buffers\n"); + dprintk(q, 3, "will sleep waiting for buffers\n"); ret = wait_event_interruptible(q->done_wq, !list_empty(&q->done_list) || !q->streaming || q->error); @@ -1790,7 +1792,7 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) call_void_qop(q, wait_finish, q); q->waiting_in_dqbuf = 0; if (ret) { - dprintk(1, "sleep was interrupted\n"); + dprintk(q, 1, "sleep was interrupted\n"); return ret; } } @@ -1838,7 +1840,7 @@ static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb, int vb2_wait_for_all_buffers(struct vb2_queue *q) { if (!q->streaming) { - dprintk(1, "streaming off, will not wait for buffers\n"); + dprintk(q, 1, "streaming off, will not wait for buffers\n"); return -EINVAL; } @@ -1876,13 +1878,13 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb, switch (vb->state) { case VB2_BUF_STATE_DONE: - dprintk(3, "returning done buffer\n"); + dprintk(q, 3, "returning done buffer\n"); break; case VB2_BUF_STATE_ERROR: - dprintk(3, "returning done buffer with errors\n"); + dprintk(q, 3, "returning done buffer with errors\n"); break; default: - dprintk(1, "invalid buffer state\n"); + dprintk(q, 1, "invalid buffer state\n"); return -EINVAL; } @@ -1913,7 +1915,7 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb, media_request_put(vb->request); vb->request = NULL; - dprintk(2, "dqbuf of buffer %d, with state %d\n", + dprintk(q, 2, "dqbuf of buffer %d, with state %d\n", vb->index, vb->state); return 0; @@ -2029,22 +2031,22 @@ int vb2_core_streamon(struct vb2_queue *q, unsigned int type) int ret; if (type != q->type) { - dprintk(1, "invalid stream type\n"); + dprintk(q, 1, "invalid stream type\n"); return -EINVAL; } if (q->streaming) { - dprintk(3, "already streaming\n"); + dprintk(q, 3, "already streaming\n"); return 0; } if (!q->num_buffers) { - dprintk(1, "no buffers have been allocated\n"); + dprintk(q, 1, "no buffers have been allocated\n"); return -EINVAL; } if (q->num_buffers < q->min_buffers_needed) { - dprintk(1, "need at least %u allocated buffers\n", + dprintk(q, 1, "need at least %u allocated buffers\n", q->min_buffers_needed); return -EINVAL; } @@ -2064,7 +2066,7 @@ int vb2_core_streamon(struct vb2_queue *q, unsigned int type) q->streaming = 1; - dprintk(3, "successful\n"); + dprintk(q, 3, "successful\n"); return 0; } EXPORT_SYMBOL_GPL(vb2_core_streamon); @@ -2080,7 +2082,7 @@ EXPORT_SYMBOL_GPL(vb2_queue_error); int vb2_core_streamoff(struct vb2_queue *q, unsigned int type) { if (type != q->type) { - dprintk(1, "invalid stream type\n"); + dprintk(q, 1, "invalid stream type\n"); return -EINVAL; } @@ -2097,7 +2099,7 @@ int vb2_core_streamoff(struct vb2_queue *q, unsigned int type) q->waiting_for_buffers = !q->is_output; q->last_buffer_dequeued = false; - dprintk(3, "successful\n"); + dprintk(q, 3, "successful\n"); return 0; } EXPORT_SYMBOL_GPL(vb2_core_streamoff); @@ -2140,39 +2142,39 @@ int vb2_core_expbuf(struct vb2_queue *q, int *fd, unsigned int type, struct dma_buf *dbuf; if (q->memory != VB2_MEMORY_MMAP) { - dprintk(1, "queue is not currently set up for mmap\n"); + dprintk(q, 1, "queue is not currently set up for mmap\n"); return -EINVAL; } if (!q->mem_ops->get_dmabuf) { - dprintk(1, "queue does not support DMA buffer exporting\n"); + dprintk(q, 1, "queue does not support DMA buffer exporting\n"); return -EINVAL; } if (flags & ~(O_CLOEXEC | O_ACCMODE)) { - dprintk(1, "queue does support only O_CLOEXEC and access mode flags\n"); + dprintk(q, 1, "queue does support only O_CLOEXEC and access mode flags\n"); return -EINVAL; } if (type != q->type) { - dprintk(1, "invalid buffer type\n"); + dprintk(q, 1, "invalid buffer type\n"); return -EINVAL; } if (index >= q->num_buffers) { - dprintk(1, "buffer index out of range\n"); + dprintk(q, 1, "buffer index out of range\n"); return -EINVAL; } vb = q->bufs[index]; if (plane >= vb->num_planes) { - dprintk(1, "buffer plane out of range\n"); + dprintk(q, 1, "buffer plane out of range\n"); return -EINVAL; } if (vb2_fileio_is_active(q)) { - dprintk(1, "expbuf: file io in progress\n"); + dprintk(q, 1, "expbuf: file io in progress\n"); return -EBUSY; } @@ -2181,20 +2183,20 @@ int vb2_core_expbuf(struct vb2_queue *q, int *fd, unsigned int type, dbuf = call_ptr_memop(vb, get_dmabuf, vb_plane->mem_priv, flags & O_ACCMODE); if (IS_ERR_OR_NULL(dbuf)) { - dprintk(1, "failed to export buffer %d, plane %d\n", + dprintk(q, 1, "failed to export buffer %d, plane %d\n", index, plane); return -EINVAL; } ret = dma_buf_fd(dbuf, flags & ~O_ACCMODE); if (ret < 0) { - dprintk(3, "buffer %d, plane %d failed to export (%d)\n", + dprintk(q, 3, "buffer %d, plane %d failed to export (%d)\n", index, plane, ret); dma_buf_put(dbuf); return ret; } - dprintk(3, "buffer %d, plane %d exported as %d descriptor\n", + dprintk(q, 3, "buffer %d, plane %d exported as %d descriptor\n", index, plane, ret); *fd = ret; @@ -2211,7 +2213,7 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) unsigned long length; if (q->memory != VB2_MEMORY_MMAP) { - dprintk(1, "queue is not currently set up for mmap\n"); + dprintk(q, 1, "queue is not currently set up for mmap\n"); return -EINVAL; } @@ -2219,17 +2221,17 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) * Check memory area access mode. */ if (!(vma->vm_flags & VM_SHARED)) { - dprintk(1, "invalid vma flags, VM_SHARED needed\n"); + dprintk(q, 1, "invalid vma flags, VM_SHARED needed\n"); return -EINVAL; } if (q->is_output) { if (!(vma->vm_flags & VM_WRITE)) { - dprintk(1, "invalid vma flags, VM_WRITE needed\n"); + dprintk(q, 1, "invalid vma flags, VM_WRITE needed\n"); return -EINVAL; } } else { if (!(vma->vm_flags & VM_READ)) { - dprintk(1, "invalid vma flags, VM_READ needed\n"); + dprintk(q, 1, "invalid vma flags, VM_READ needed\n"); return -EINVAL; } } @@ -2237,7 +2239,7 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) mutex_lock(&q->mmap_lock); if (vb2_fileio_is_active(q)) { - dprintk(1, "mmap: file io in progress\n"); + dprintk(q, 1, "mmap: file io in progress\n"); ret = -EBUSY; goto unlock; } @@ -2258,7 +2260,7 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) */ length = PAGE_ALIGN(vb->planes[plane].length); if (length < (vma->vm_end - vma->vm_start)) { - dprintk(1, + dprintk(q, 1, "MMAP invalid, as it would overflow buffer length\n"); ret = -EINVAL; goto unlock; @@ -2278,7 +2280,7 @@ unlock: if (ret) return ret; - dprintk(3, "buffer %d, plane %d successfully mapped\n", buffer, plane); + dprintk(q, 3, "buffer %d, plane %d successfully mapped\n", buffer, plane); return 0; } EXPORT_SYMBOL_GPL(vb2_mmap); @@ -2297,7 +2299,7 @@ unsigned long vb2_get_unmapped_area(struct vb2_queue *q, int ret; if (q->memory != VB2_MEMORY_MMAP) { - dprintk(1, "queue is not currently set up for mmap\n"); + dprintk(q, 1, "queue is not currently set up for mmap\n"); return -EINVAL; } @@ -2349,6 +2351,10 @@ int vb2_core_queue_init(struct vb2_queue *q) else q->dma_dir = q->is_output ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + if (q->name[0] == '\0') + snprintf(q->name, sizeof(q->name), "%s-%p", + q->is_output ? "out" : "cap", q); + return 0; } EXPORT_SYMBOL_GPL(vb2_core_queue_init); @@ -2537,7 +2543,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) */ count = 1; - dprintk(3, "setting up file io: mode %s, count %d, read_once %d, write_immediately %d\n", + dprintk(q, 3, "setting up file io: mode %s, count %d, read_once %d, write_immediately %d\n", (read) ? "read" : "write", count, q->fileio_read_once, q->fileio_write_immediately); @@ -2635,7 +2641,7 @@ static int __vb2_cleanup_fileio(struct vb2_queue *q) fileio->count = 0; vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count); kfree(fileio); - dprintk(3, "file io emulator closed\n"); + dprintk(q, 3, "file io emulator closed\n"); } return 0; } @@ -2664,7 +2670,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ unsigned index; int ret; - dprintk(3, "mode %s, offset %ld, count %zd, %sblocking\n", + dprintk(q, 3, "mode %s, offset %ld, count %zd, %sblocking\n", read ? "read" : "write", (long)*ppos, count, nonblock ? "non" : ""); @@ -2672,7 +2678,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ return -EINVAL; if (q->waiting_in_dqbuf) { - dprintk(3, "another dup()ped fd is %s\n", + dprintk(q, 3, "another dup()ped fd is %s\n", read ? "reading" : "writing"); return -EBUSY; } @@ -2682,7 +2688,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ */ if (!vb2_fileio_is_active(q)) { ret = __vb2_init_fileio(q, read); - dprintk(3, "vb2_init_fileio result: %d\n", ret); + dprintk(q, 3, "vb2_init_fileio result: %d\n", ret); if (ret) return ret; } @@ -2699,7 +2705,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ * Call vb2_dqbuf to get buffer back. */ ret = vb2_core_dqbuf(q, &index, NULL, nonblock); - dprintk(5, "vb2_dqbuf result: %d\n", ret); + dprintk(q, 5, "vb2_dqbuf result: %d\n", ret); if (ret) return ret; fileio->dq_count += 1; @@ -2730,20 +2736,20 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ */ if (buf->pos + count > buf->size) { count = buf->size - buf->pos; - dprintk(5, "reducing read count: %zd\n", count); + dprintk(q, 5, "reducing read count: %zd\n", count); } /* * Transfer data to userspace. */ - dprintk(3, "copying %zd bytes - buffer %d, offset %u\n", + dprintk(q, 3, "copying %zd bytes - buffer %d, offset %u\n", count, index, buf->pos); if (read) ret = copy_to_user(data, buf->vaddr + buf->pos, count); else ret = copy_from_user(buf->vaddr + buf->pos, data, count); if (ret) { - dprintk(3, "error copying data\n"); + dprintk(q, 3, "error copying data\n"); return -EFAULT; } @@ -2763,7 +2769,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ * Check if this is the last buffer to read. */ if (read && fileio->read_once && fileio->dq_count == 1) { - dprintk(3, "read limit reached\n"); + dprintk(q, 3, "read limit reached\n"); return __vb2_cleanup_fileio(q); } @@ -2775,7 +2781,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ if (copy_timestamp) b->timestamp = ktime_get_ns(); ret = vb2_core_qbuf(q, index, NULL, NULL); - dprintk(5, "vb2_dbuf result: %d\n", ret); + dprintk(q, 5, "vb2_dbuf result: %d\n", ret); if (ret) return ret; @@ -2862,7 +2868,7 @@ static int vb2_thread(void *data) if (!threadio->stop) ret = vb2_core_dqbuf(q, &index, NULL, 0); call_void_qop(q, wait_prepare, q); - dprintk(5, "file io: vb2_dqbuf result: %d\n", ret); + dprintk(q, 5, "file io: vb2_dqbuf result: %d\n", ret); if (!ret) vb = q->bufs[index]; } @@ -2916,7 +2922,7 @@ int vb2_thread_start(struct vb2_queue *q, vb2_thread_fnc fnc, void *priv, threadio->priv = priv; ret = __vb2_init_fileio(q, !q->is_output); - dprintk(3, "file io: vb2_init_fileio result: %d\n", ret); + dprintk(q, 3, "file io: vb2_init_fileio result: %d\n", ret); if (ret) goto nomem; q->threadio = threadio; diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 559a229cac41..98477a82c810 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -35,10 +35,11 @@ static int debug; module_param(debug, int, 0644); -#define dprintk(level, fmt, arg...) \ +#define dprintk(q, level, fmt, arg...) \ do { \ if (debug >= level) \ - pr_info("vb2-v4l2: %s: " fmt, __func__, ## arg); \ + pr_info("vb2-v4l2: [%p] %s: " fmt, \ + (q)->name, __func__, ## arg); \ } while (0) /* Flags that are set by us */ @@ -66,12 +67,14 @@ static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer /* Is memory for copying plane information present? */ if (b->m.planes == NULL) { - dprintk(1, "multi-planar buffer passed but planes array not provided\n"); + dprintk(vb->vb2_queue, 1, + "multi-planar buffer passed but planes array not provided\n"); return -EINVAL; } if (b->length < vb->num_planes || b->length > VB2_MAX_PLANES) { - dprintk(1, "incorrect planes array length, expected %d, got %d\n", + dprintk(vb->vb2_queue, 1, + "incorrect planes array length, expected %d, got %d\n", vb->num_planes, b->length); return -EINVAL; } @@ -179,7 +182,7 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b ret = __verify_length(vb, b); if (ret < 0) { - dprintk(1, "plane parameters verification failed: %d\n", ret); + dprintk(q, 1, "plane parameters verification failed: %d\n", ret); return ret; } if (b->field == V4L2_FIELD_ALTERNATE && q->is_output) { @@ -192,7 +195,7 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b * that just says that it is either a top or a bottom field, * but not which of the two it is. */ - dprintk(1, "the field is incorrectly set to ALTERNATE for an output buffer\n"); + dprintk(q, 1, "the field is incorrectly set to ALTERNATE for an output buffer\n"); return -EINVAL; } vbuf->sequence = 0; @@ -395,23 +398,23 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md int ret; if (b->type != q->type) { - dprintk(1, "%s: invalid buffer type\n", opname); + dprintk(q, 1, "%s: invalid buffer type\n", opname); return -EINVAL; } if (b->index >= q->num_buffers) { - dprintk(1, "%s: buffer index out of range\n", opname); + dprintk(q, 1, "%s: buffer index out of range\n", opname); return -EINVAL; } if (q->bufs[b->index] == NULL) { /* Should never happen */ - dprintk(1, "%s: buffer is NULL\n", opname); + dprintk(q, 1, "%s: buffer is NULL\n", opname); return -EINVAL; } if (b->memory != q->memory) { - dprintk(1, "%s: invalid memory type\n", opname); + dprintk(q, 1, "%s: invalid memory type\n", opname); return -EINVAL; } @@ -423,7 +426,7 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md if (!is_prepare && (b->flags & V4L2_BUF_FLAG_REQUEST_FD) && vb->state != VB2_BUF_STATE_DEQUEUED) { - dprintk(1, "%s: buffer is not in dequeued state\n", opname); + dprintk(q, 1, "%s: buffer is not in dequeued state\n", opname); return -EINVAL; } @@ -442,19 +445,19 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md if (!(b->flags & V4L2_BUF_FLAG_REQUEST_FD)) { if (q->requires_requests) { - dprintk(1, "%s: queue requires requests\n", opname); + dprintk(q, 1, "%s: queue requires requests\n", opname); return -EBADR; } if (q->uses_requests) { - dprintk(1, "%s: queue uses requests\n", opname); + dprintk(q, 1, "%s: queue uses requests\n", opname); return -EBUSY; } return 0; } else if (!q->supports_requests) { - dprintk(1, "%s: queue does not support requests\n", opname); + dprintk(q, 1, "%s: queue does not support requests\n", opname); return -EBADR; } else if (q->uses_qbuf) { - dprintk(1, "%s: queue does not use requests\n", opname); + dprintk(q, 1, "%s: queue does not use requests\n", opname); return -EBUSY; } @@ -484,13 +487,13 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md return -EINVAL; if (b->request_fd < 0) { - dprintk(1, "%s: request_fd < 0\n", opname); + dprintk(q, 1, "%s: request_fd < 0\n", opname); return -EINVAL; } req = media_request_get_by_fd(mdev, b->request_fd); if (IS_ERR(req)) { - dprintk(1, "%s: invalid request_fd\n", opname); + dprintk(q, 1, "%s: invalid request_fd\n", opname); return PTR_ERR(req); } @@ -500,7 +503,7 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md */ if (req->state != MEDIA_REQUEST_STATE_IDLE && req->state != MEDIA_REQUEST_STATE_UPDATING) { - dprintk(1, "%s: request is not idle\n", opname); + dprintk(q, 1, "%s: request is not idle\n", opname); media_request_put(req); return -EBUSY; } @@ -683,12 +686,12 @@ int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b) int ret; if (b->type != q->type) { - dprintk(1, "wrong buffer type\n"); + dprintk(q, 1, "wrong buffer type\n"); return -EINVAL; } if (b->index >= q->num_buffers) { - dprintk(1, "buffer index out of range\n"); + dprintk(q, 1, "buffer index out of range\n"); return -EINVAL; } vb = q->bufs[b->index]; @@ -743,7 +746,7 @@ int vb2_prepare_buf(struct vb2_queue *q, struct media_device *mdev, int ret; if (vb2_fileio_is_active(q)) { - dprintk(1, "file io in progress\n"); + dprintk(q, 1, "file io in progress\n"); return -EBUSY; } @@ -823,7 +826,7 @@ int vb2_qbuf(struct vb2_queue *q, struct media_device *mdev, int ret; if (vb2_fileio_is_active(q)) { - dprintk(1, "file io in progress\n"); + dprintk(q, 1, "file io in progress\n"); return -EBUSY; } @@ -842,12 +845,12 @@ int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) int ret; if (vb2_fileio_is_active(q)) { - dprintk(1, "file io in progress\n"); + dprintk(q, 1, "file io in progress\n"); return -EBUSY; } if (b->type != q->type) { - dprintk(1, "invalid buffer type\n"); + dprintk(q, 1, "invalid buffer type\n"); return -EINVAL; } @@ -871,7 +874,7 @@ EXPORT_SYMBOL_GPL(vb2_dqbuf); int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) { if (vb2_fileio_is_active(q)) { - dprintk(1, "file io in progress\n"); + dprintk(q, 1, "file io in progress\n"); return -EBUSY; } return vb2_core_streamon(q, type); @@ -881,7 +884,7 @@ EXPORT_SYMBOL_GPL(vb2_streamon); int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) { if (vb2_fileio_is_active(q)) { - dprintk(1, "file io in progress\n"); + dprintk(q, 1, "file io in progress\n"); return -EBUSY; } return vb2_core_streamoff(q, type); @@ -895,7 +898,7 @@ int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb) } EXPORT_SYMBOL_GPL(vb2_expbuf); -int vb2_queue_init(struct vb2_queue *q) +int vb2_queue_init_name(struct vb2_queue *q, const char *name) { /* * Sanity check @@ -931,8 +934,19 @@ int vb2_queue_init(struct vb2_queue *q) */ q->quirk_poll_must_check_waiting_for_buffers = true; + if (name) + strscpy(q->name, name, sizeof(q->name)); + else + q->name[0] = '\0'; + return vb2_core_queue_init(q); } +EXPORT_SYMBOL_GPL(vb2_queue_init_name); + +int vb2_queue_init(struct vb2_queue *q) +{ + return vb2_queue_init_name(q, NULL); +} EXPORT_SYMBOL_GPL(vb2_queue_init); void vb2_queue_release(struct vb2_queue *q) diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 9e68fe043a6c..52ef92049073 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -561,6 +561,8 @@ struct vb2_buf_ops { * when a buffer with the %V4L2_BUF_FLAG_LAST is dequeued. * @fileio: file io emulator internal data, used only if emulator is active * @threadio: thread io internal data, used only if thread is active + * @name: queue name, used for logging purpose. Initialized automatically + * if left empty by drivers. */ struct vb2_queue { unsigned int type; @@ -622,6 +624,8 @@ struct vb2_queue { struct vb2_fileio_data *fileio; struct vb2_threadio_data *threadio; + char name[32]; + #ifdef CONFIG_VIDEO_ADV_DEBUG /* * Counters for how often these queue-related ops are diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h index 59bf33a12648..b7b5a9cb5a28 100644 --- a/include/media/videobuf2-v4l2.h +++ b/include/media/videobuf2-v4l2.h @@ -236,6 +236,19 @@ int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type); */ int __must_check vb2_queue_init(struct vb2_queue *q); +/** + * vb2_queue_init_name() - initialize a videobuf2 queue with a name + * @q: pointer to &struct vb2_queue with videobuf2 queue. + * @name: the queue name + * + * This function initializes the vb2_queue exactly like vb2_queue_init(), + * and additionally sets the queue name. The queue name is used for logging + * purpose, and should uniquely identify the queue within the context of the + * device it belongs to. This is useful to attribute kernel log messages to the + * right queue for m2m devices or other devices that handle multiple queues. + */ +int __must_check vb2_queue_init_name(struct vb2_queue *q, const char *name); + /** * vb2_queue_release() - stop streaming, release the queue and free memory * @q: pointer to &struct vb2_queue with videobuf2 queue. -- cgit v1.2.3 From 2f00f3a4916c546582060d114b4b96286ab3451e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 17 Jun 2020 17:27:09 +0200 Subject: media: samsung: Rename Samsung and Exynos to lowercase Fix up inconsistent usage of upper and lowercase letters in "Samsung" and "Exynos" names. "SAMSUNG" and "EXYNOS" are not abbreviations but regular trademarked names. Therefore they should be written with lowercase letters starting with capital letter. The lowercase "Exynos" name is promoted by its manufacturer Samsung Electronics Co., Ltd., in advertisement materials and on website. Although advertisement materials usually use uppercase "SAMSUNG", the lowercase version is used in all legal aspects (e.g. on Wikipedia and in privacy/legal statements on https://www.samsung.com/semiconductor/privacy-global/). Signed-off-by: Krzysztof Kozlowski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/admin-guide/media/fimc.rst | 6 +++--- Documentation/driver-api/media/drivers/tuners.rst | 2 +- drivers/media/platform/exynos4-is/media-dev.c | 2 +- drivers/media/platform/s3c-camif/camif-core.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/Documentation/admin-guide/media/fimc.rst b/Documentation/admin-guide/media/fimc.rst index 0b8ddc4a3008..56b149d9a527 100644 --- a/Documentation/admin-guide/media/fimc.rst +++ b/Documentation/admin-guide/media/fimc.rst @@ -2,7 +2,7 @@ .. include:: -The Samsung S5P/EXYNOS4 FIMC driver +The Samsung S5P/Exynos4 FIMC driver =================================== Copyright |copy| 2012 - 2013 Samsung Electronics Co., Ltd. @@ -19,7 +19,7 @@ drivers/media/platform/exynos4-is directory. Supported SoCs -------------- -S5PC100 (mem-to-mem only), S5PV210, EXYNOS4210 +S5PC100 (mem-to-mem only), S5PV210, Exynos4210 Supported features ------------------ @@ -45,7 +45,7 @@ Media device interface ~~~~~~~~~~~~~~~~~~~~~~ The driver supports Media Controller API as defined at :ref:`media_controller`. -The media device driver name is "SAMSUNG S5P FIMC". +The media device driver name is "Samsung S5P FIMC". The purpose of this interface is to allow changing assignment of FIMC instances to the SoC peripheral camera input at runtime and optionally to control internal diff --git a/Documentation/driver-api/media/drivers/tuners.rst b/Documentation/driver-api/media/drivers/tuners.rst index 7509be888909..d7924141c544 100644 --- a/Documentation/driver-api/media/drivers/tuners.rst +++ b/Documentation/driver-api/media/drivers/tuners.rst @@ -18,7 +18,7 @@ These differ mainly by the bandswitch byte. Tuner Manufacturers ------------------- -- SAMSUNG Tuner identification: (e.g. TCPM9091PD27) +- Samsung Tuner identification: (e.g. TCPM9091PD27) .. code-block:: none diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 9aaf3b8060d5..96e336b19cc3 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -1439,7 +1439,7 @@ static int fimc_md_probe(struct platform_device *pdev) INIT_LIST_HEAD(&fmd->pipelines); fmd->pdev = pdev; - strscpy(fmd->media_dev.model, "SAMSUNG S5P FIMC", + strscpy(fmd->media_dev.model, "Samsung S5P FIMC", sizeof(fmd->media_dev.model)); fmd->media_dev.ops = &fimc_md_ops; fmd->media_dev.dev = dev; diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c index c6fbcd7036d6..92f43c0cbc0c 100644 --- a/drivers/media/platform/s3c-camif/camif-core.c +++ b/drivers/media/platform/s3c-camif/camif-core.c @@ -304,7 +304,7 @@ static int camif_media_dev_init(struct camif_dev *camif) int ret; memset(md, 0, sizeof(*md)); - snprintf(md->model, sizeof(md->model), "SAMSUNG S3C%s CAMIF", + snprintf(md->model, sizeof(md->model), "Samsung S3C%s CAMIF", ip_rev == S3C6410_CAMIF_IP_REV ? "6410" : "244X"); strscpy(md->bus_info, "platform", sizeof(md->bus_info)); md->hw_revision = ip_rev; -- cgit v1.2.3 From 961d3b27a2aff52dda0b97d35085a743a27c2f46 Mon Sep 17 00:00:00 2001 From: John Cox Date: Wed, 17 Jun 2020 15:21:52 +0200 Subject: media: videobuf2: Fix length check for single plane dmabuf queueing Check against length in v4l2_buffer rather than vb2_buffer when the buffer is a dmabuf. This makes the single plane test the same as the existing multiplanar test. Signed-off-by: John Cox Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-v4l2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 98477a82c810..57aa183bd198 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -117,7 +117,8 @@ static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b) return -EINVAL; } } else { - length = (b->memory == VB2_MEMORY_USERPTR) + length = (b->memory == VB2_MEMORY_USERPTR || + b->memory == VB2_MEMORY_DMABUF) ? b->length : vb->planes[0].length; if (b->bytesused > length) -- cgit v1.2.3 From a32e8adf2fdbd4fd1d046769140a8afd0489fe65 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 15 Jun 2020 23:40:15 +0200 Subject: media: test_drivers: vivid-core: Use array_size() helper The tpg_alloc() and vmalloc() functions have no 2-factor argument form, so multiplication factors need to be wrapped in array_size(). This issue was found with the help of Coccinelle and, audited and fixed manually. Addresses-KSPP-ID: https://github.com/KSPP/linux/issues/83 Signed-off-by: Gustavo A. R. Silva Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/test-drivers/vivid/vivid-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c index 5c986df4a8d4..f7ee37e9508d 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.c +++ b/drivers/media/test-drivers/vivid/vivid-core.c @@ -1126,7 +1126,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) ret = -ENOMEM; /* initialize the test pattern generator */ tpg_init(&dev->tpg, 640, 360); - if (tpg_alloc(&dev->tpg, MAX_ZOOM * MAX_WIDTH)) + if (tpg_alloc(&dev->tpg, array_size(MAX_WIDTH, MAX_ZOOM))) goto free_dev; dev->scaled_line = vzalloc(array_size(MAX_WIDTH, MAX_ZOOM)); if (!dev->scaled_line) @@ -1136,7 +1136,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) goto free_dev; /* load the edid */ - dev->edid = vmalloc(256 * 128); + dev->edid = vmalloc(array_size(256, 128)); if (!dev->edid) goto free_dev; -- cgit v1.2.3 From ec050e022f8da39c2c9d6f4eb8c7f343fe2664ad Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Thu, 11 Jun 2020 21:32:32 +0200 Subject: media: rcar-csi2: Rename confirm_start() to phy_post_init() to match its usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the driver was picked-up the starting of the PHY have changed quiet a bit. An artifact of these changes is the now poorly named callback confirm_start(). It used to confirm start of the PHY but now performs post PHY start initialization, rename it to phy_post_init() to reflect this. Signed-off-by: Niklas Söderlund Acked-by: Jacopo Mondi Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-csi2.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c index 45fdf4e74e51..c6cc4f473a07 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -345,7 +345,7 @@ enum rcar_csi2_pads { struct rcar_csi2_info { int (*init_phtw)(struct rcar_csi2 *priv, unsigned int mbps); - int (*confirm_start)(struct rcar_csi2 *priv); + int (*phy_post_init)(struct rcar_csi2 *priv); const struct rcsi2_mbps_reg *hsfreqrange; unsigned int csi0clkfreqrange; unsigned int num_channels; @@ -576,9 +576,9 @@ static int rcsi2_start_receiver(struct rcar_csi2 *priv) if (ret) return ret; - /* Confirm start */ - if (priv->info->confirm_start) { - ret = priv->info->confirm_start(priv); + /* Run post PHY start initialization, if needed. */ + if (priv->info->phy_post_init) { + ret = priv->info->phy_post_init(priv); if (ret) return ret; } @@ -976,7 +976,7 @@ static int rcsi2_init_phtw_v3m_e3(struct rcar_csi2 *priv, unsigned int mbps) return rcsi2_phtw_write_mbps(priv, mbps, phtw_mbps_v3m_e3, 0x44); } -static int rcsi2_confirm_start_v3m_e3(struct rcar_csi2 *priv) +static int rcsi2_phy_post_init_v3m_e3(struct rcar_csi2 *priv) { static const struct phtw_value step1[] = { { .data = 0xee, .code = 0x34 }, @@ -1060,7 +1060,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77965 = { static const struct rcar_csi2_info rcar_csi2_info_r8a77970 = { .init_phtw = rcsi2_init_phtw_v3m_e3, - .confirm_start = rcsi2_confirm_start_v3m_e3, + .phy_post_init = rcsi2_phy_post_init_v3m_e3, .num_channels = 4, }; @@ -1073,7 +1073,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77980 = { static const struct rcar_csi2_info rcar_csi2_info_r8a77990 = { .init_phtw = rcsi2_init_phtw_v3m_e3, - .confirm_start = rcsi2_confirm_start_v3m_e3, + .phy_post_init = rcsi2_phy_post_init_v3m_e3, .num_channels = 2, }; -- cgit v1.2.3 From 477d4d38b93d457d6391a864dcfcc114a8b44060 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 10 Jun 2020 19:26:15 +0200 Subject: media: pxa_camera: remove an impossible condition It's not possible for "pcdev->mclk" to be zero because we check for that earlier and set it to 20000000 in that situation. If it were possible, that would be a problem because it could have lead to an Oops in the error handling when we call v4l2_clk_unregister(pcdev->mclk_clk); Signed-off-by: Dan Carpenter Acked-by: Robert Jarzmik Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/pxa_camera.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index a7cf830235b6..6dce33f35041 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -2504,17 +2504,14 @@ static int pxa_camera_probe(struct platform_device *pdev) if (err) goto exit_notifier_cleanup; - if (pcdev->mclk) { - v4l2_clk_name_i2c(clk_name, sizeof(clk_name), - pcdev->asd.match.i2c.adapter_id, - pcdev->asd.match.i2c.address); - - pcdev->mclk_clk = v4l2_clk_register(&pxa_camera_mclk_ops, - clk_name, NULL); - if (IS_ERR(pcdev->mclk_clk)) { - err = PTR_ERR(pcdev->mclk_clk); - goto exit_notifier_cleanup; - } + v4l2_clk_name_i2c(clk_name, sizeof(clk_name), + pcdev->asd.match.i2c.adapter_id, + pcdev->asd.match.i2c.address); + + pcdev->mclk_clk = v4l2_clk_register(&pxa_camera_mclk_ops, clk_name, NULL); + if (IS_ERR(pcdev->mclk_clk)) { + err = PTR_ERR(pcdev->mclk_clk); + goto exit_notifier_cleanup; } err = v4l2_async_notifier_register(&pcdev->v4l2_dev, &pcdev->notifier); -- cgit v1.2.3 From d000e9b5e4a23dd700b3f58a4738c94bb5179ff0 Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Sat, 6 Jun 2020 16:39:18 +0200 Subject: media: tvp5150: Add missed media_entity_cleanup() This driver does not call media_entity_cleanup() in the error handler of tvp5150_registered() and tvp5150_remove(), while it has called media_entity_pads_init() at first. Add the missed calls to fix it. Fixes: 0556f1d580d4 ("media: tvp5150: add input source selection of_graph support") Signed-off-by: Chuhong Yuan Reviewed-by: Marco Felsch Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tvp5150.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index eb39cf5ea089..9df575238952 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1664,8 +1664,10 @@ static int tvp5150_registered(struct v4l2_subdev *sd) return 0; err: - for (i = 0; i < decoder->connectors_num; i++) + for (i = 0; i < decoder->connectors_num; i++) { media_device_unregister_entity(&decoder->connectors[i].ent); + media_entity_cleanup(&decoder->connectors[i].ent); + } return ret; #endif @@ -2248,8 +2250,10 @@ static int tvp5150_remove(struct i2c_client *c) for (i = 0; i < decoder->connectors_num; i++) v4l2_fwnode_connector_free(&decoder->connectors[i].base); - for (i = 0; i < decoder->connectors_num; i++) + for (i = 0; i < decoder->connectors_num; i++) { media_device_unregister_entity(&decoder->connectors[i].ent); + media_entity_cleanup(&decoder->connectors[i].ent); + } v4l2_async_unregister_subdev(sd); v4l2_ctrl_handler_free(&decoder->hdl); pm_runtime_disable(&c->dev); -- cgit v1.2.3 From 18ffec750578f7447c288647d7282c7d12b1d969 Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Thu, 28 May 2020 08:41:47 +0200 Subject: media: exynos4-is: Add missed check for pinctrl_lookup_state() fimc_md_get_pinctrl() misses a check for pinctrl_lookup_state(). Add the missed check to fix it. Fixes: 4163851f7b99 ("[media] s5p-fimc: Use pinctrl API for camera ports configuration]") Signed-off-by: Chuhong Yuan Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/media-dev.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 96e336b19cc3..16dd660137a8 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -1270,6 +1270,9 @@ static int fimc_md_get_pinctrl(struct fimc_md *fmd) pctl->state_idle = pinctrl_lookup_state(pctl->pinctrl, PINCTRL_STATE_IDLE); + if (IS_ERR(pctl->state_idle)) + return PTR_ERR(pctl->state_idle); + return 0; } -- cgit v1.2.3 From ded874ece29d3fe2abd3775810a06056067eb68c Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 30 Apr 2020 18:42:44 +0200 Subject: media: rockchip: rga: Introduce color fmt macros and refactor CSC mode logic This introduces two macros: RGA_COLOR_FMT_IS_YUV and RGA_COLOR_FMT_IS_RGB which allow quick checking of the colorspace familily of a RGA color format. These macros are then used to refactor the logic for CSC mode selection. The two nested tests for input colorspace are simplified into a single one, with a logical and, making the whole more readable. Signed-off-by: Paul Kocialkowski Reviewed-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rockchip/rga/rga-hw.c | 23 ++++++++++------------- drivers/media/platform/rockchip/rga/rga-hw.h | 5 +++++ 2 files changed, 15 insertions(+), 13 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 4be6dcf292ff..5607ee8d1917 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -200,22 +200,19 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) dst_info.data.format = ctx->out.fmt->hw_format; dst_info.data.swap = ctx->out.fmt->color_swap; - if (ctx->in.fmt->hw_format >= RGA_COLOR_FMT_YUV422SP) { - if (ctx->out.fmt->hw_format < RGA_COLOR_FMT_YUV422SP) { - switch (ctx->in.colorspace) { - case V4L2_COLORSPACE_REC709: - src_info.data.csc_mode = - RGA_SRC_CSC_MODE_BT709_R0; - break; - default: - src_info.data.csc_mode = - RGA_SRC_CSC_MODE_BT601_R0; - break; - } + if (RGA_COLOR_FMT_IS_YUV(ctx->in.fmt->hw_format) && + RGA_COLOR_FMT_IS_RGB(ctx->out.fmt->hw_format)) { + switch (ctx->in.colorspace) { + case V4L2_COLORSPACE_REC709: + src_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0; + break; + default: + src_info.data.csc_mode = RGA_SRC_CSC_MODE_BT601_R0; + break; } } - if (ctx->out.fmt->hw_format >= RGA_COLOR_FMT_YUV422SP) { + if (RGA_COLOR_FMT_IS_YUV(ctx->out.fmt->hw_format)) { switch (ctx->out.colorspace) { case V4L2_COLORSPACE_REC709: dst_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0; diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h index 96cb0314dfa7..e8917e5630a4 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.h +++ b/drivers/media/platform/rockchip/rga/rga-hw.h @@ -95,6 +95,11 @@ #define RGA_COLOR_FMT_CP_8BPP 15 #define RGA_COLOR_FMT_MASK 15 +#define RGA_COLOR_FMT_IS_YUV(fmt) \ + (((fmt) >= RGA_COLOR_FMT_YUV422SP) && ((fmt) < RGA_COLOR_FMT_CP_1BPP)) +#define RGA_COLOR_FMT_IS_RGB(fmt) \ + ((fmt) < RGA_COLOR_FMT_YUV422SP) + #define RGA_COLOR_NONE_SWAP 0 #define RGA_COLOR_RB_SWAP 1 #define RGA_COLOR_ALPHA_SWAP 2 -- cgit v1.2.3 From 0f879bab72f47e8ba2421a984e7acfa763d3e84e Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 30 Apr 2020 18:42:45 +0200 Subject: media: rockchip: rga: Only set output CSC mode for RGB input Setting the output CSC mode is required for a YUV output, but must not be set when the input is also YUV. Doing this (as tested with a YUV420P to YUV420P conversion) results in wrong colors. Adapt the logic to only set the output CSC mode when the output is YUV and the input is RGB. Also add a comment to clarify the rationale. Fixes: f7e7b48e6d79 ("[media] rockchip/rga: v4l2 m2m support") Signed-off-by: Paul Kocialkowski Reviewed-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rockchip/rga/rga-hw.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 5607ee8d1917..aaa96f256356 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -200,6 +200,11 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) dst_info.data.format = ctx->out.fmt->hw_format; dst_info.data.swap = ctx->out.fmt->color_swap; + /* + * CSC mode must only be set when the colorspace families differ between + * input and output. It must remain unset (zeroed) if both are the same. + */ + if (RGA_COLOR_FMT_IS_YUV(ctx->in.fmt->hw_format) && RGA_COLOR_FMT_IS_RGB(ctx->out.fmt->hw_format)) { switch (ctx->in.colorspace) { @@ -212,7 +217,8 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) } } - if (RGA_COLOR_FMT_IS_YUV(ctx->out.fmt->hw_format)) { + if (RGA_COLOR_FMT_IS_RGB(ctx->in.fmt->hw_format) && + RGA_COLOR_FMT_IS_YUV(ctx->out.fmt->hw_format)) { switch (ctx->out.colorspace) { case V4L2_COLORSPACE_REC709: dst_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0; -- cgit v1.2.3 From 6f01dfb760c027d5dd6199d91ee9599f2676b5c6 Mon Sep 17 00:00:00 2001 From: Dariusz Marcinkiewicz Date: Mon, 22 Jun 2020 13:46:36 +0200 Subject: media: cros-ec-cec: do not bail on device_init_wakeup failure Do not fail probing when device_init_wakeup fails. device_init_wakeup fails when the device is already enabled as wakeup device. Hence, the driver fails to probe the device if: - The device has already been enabled for wakeup (by e.g. sysfs) - The driver has been unloaded and is being loaded again. This goal of the patch is to fix the above cases. Overwhelming majority of the drivers do not check device_init_wakeup return code. Fixes: cd70de2d356ee ("media: platform: Add ChromeOS EC CEC driver") Signed-off-by: Dariusz Marcinkiewicz Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/platform/cros-ec/cros-ec-cec.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c index 0e7e2772f08f..2d95e16cd248 100644 --- a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c +++ b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c @@ -277,11 +277,7 @@ static int cros_ec_cec_probe(struct platform_device *pdev) platform_set_drvdata(pdev, cros_ec_cec); cros_ec_cec->cros_ec = cros_ec; - ret = device_init_wakeup(&pdev->dev, 1); - if (ret) { - dev_err(&pdev->dev, "failed to initialize wakeup\n"); - return ret; - } + device_init_wakeup(&pdev->dev, 1); cros_ec_cec->adap = cec_allocate_adapter(&cros_ec_cec_ops, cros_ec_cec, DRV_NAME, -- cgit v1.2.3 From 137641287eb40260783a4413847a0aef06023a6c Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Tue, 5 May 2020 12:50:33 +0200 Subject: go7007: add sanity checking for endpoints A malicious USB device may lack endpoints the driver assumes to exist Accessing them leads to NULL pointer accesses. This patch introduces sanity checking. Reported-and-tested-by: syzbot+cabfa4b5b05ff6be4ef0@syzkaller.appspotmail.com Signed-off-by: Oliver Neukum Fixes: 866b8695d67e8 ("Staging: add the go7007 video driver") Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/go7007/go7007-usb.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/go7007/go7007-usb.c b/drivers/media/usb/go7007/go7007-usb.c index f889c9d740cd..dbf0455d5d50 100644 --- a/drivers/media/usb/go7007/go7007-usb.c +++ b/drivers/media/usb/go7007/go7007-usb.c @@ -1132,6 +1132,10 @@ static int go7007_usb_probe(struct usb_interface *intf, go->hpi_ops = &go7007_usb_onboard_hpi_ops; go->hpi_context = usb; + ep = usb->usbdev->ep_in[4]; + if (!ep) + return -ENODEV; + /* Allocate the URB and buffer for receiving incoming interrupts */ usb->intr_urb = usb_alloc_urb(0, GFP_KERNEL); if (usb->intr_urb == NULL) @@ -1141,7 +1145,6 @@ static int go7007_usb_probe(struct usb_interface *intf, if (usb->intr_urb->transfer_buffer == NULL) goto allocfail; - ep = usb->usbdev->ep_in[4]; if (usb_endpoint_type(&ep->desc) == USB_ENDPOINT_XFER_BULK) usb_fill_bulk_urb(usb->intr_urb, usb->usbdev, usb_rcvbulkpipe(usb->usbdev, 4), @@ -1263,9 +1266,13 @@ static int go7007_usb_probe(struct usb_interface *intf, /* Allocate the URBs and buffers for receiving the video stream */ if (board->flags & GO7007_USB_EZUSB) { + if (!usb->usbdev->ep_in[6]) + goto allocfail; v_urb_len = 1024; video_pipe = usb_rcvbulkpipe(usb->usbdev, 6); } else { + if (!usb->usbdev->ep_in[1]) + goto allocfail; v_urb_len = 512; video_pipe = usb_rcvbulkpipe(usb->usbdev, 1); } @@ -1285,6 +1292,8 @@ static int go7007_usb_probe(struct usb_interface *intf, /* Allocate the URBs and buffers for receiving the audio stream */ if ((board->flags & GO7007_USB_EZUSB) && (board->main_info.flags & GO7007_BOARD_HAS_AUDIO)) { + if (!usb->usbdev->ep_in[8]) + goto allocfail; for (i = 0; i < 8; ++i) { usb->audio_urbs[i] = usb_alloc_urb(0, GFP_KERNEL); if (usb->audio_urbs[i] == NULL) -- cgit v1.2.3 From 6c42227c3467549ddc65efe99c869021d2f4a570 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 26 Jun 2020 12:44:26 +0200 Subject: cec-api: prevent leaking memory through hole in structure Fix this smatch warning: drivers/media/cec/core/cec-api.c:156 cec_adap_g_log_addrs() warn: check that 'log_addrs' doesn't leak information (struct has a hole after 'features') Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/core/cec-api.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/cec/core/cec-api.c b/drivers/media/cec/core/cec-api.c index 17d1cb2e5f97..f922a2196b2b 100644 --- a/drivers/media/cec/core/cec-api.c +++ b/drivers/media/cec/core/cec-api.c @@ -147,7 +147,13 @@ static long cec_adap_g_log_addrs(struct cec_adapter *adap, struct cec_log_addrs log_addrs; mutex_lock(&adap->lock); - log_addrs = adap->log_addrs; + /* + * We use memcpy here instead of assignment since there is a + * hole at the end of struct cec_log_addrs that an assignment + * might ignore. So when we do copy_to_user() we could leak + * one byte of memory. + */ + memcpy(&log_addrs, &adap->log_addrs, sizeof(log_addrs)); if (!adap->is_configured) memset(log_addrs.log_addr, CEC_LOG_ADDR_INVALID, sizeof(log_addrs.log_addr)); -- cgit v1.2.3 From a69a7a33268308ddcc9abf0f7d7cd61ec4300cbe Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 1 Jul 2020 15:17:30 +0200 Subject: v4l2-core: Print control name in VIDIOC_S/G_(EXT)_CTRL(S) While debugging, it's currently really hard to identify controls by their ID. Print the control name making the print more helpful. With this change, the print changes from: video1: VIDIOC_S_EXT_CTRLS: which=0xf010000, count=5, error_idx=4, request_fd=45, id/size=0x990ce8/1048, id/size=0x990ce9/12, id/size=0x990cea/480, id/size=0x990ceb/896, id/size=0x990cec/400 video1: VIDIOC_S_EXT_CTRLS: which=0xf010000, count=5, error_idx=4, request_fd=42, name=H264 Sequence Parameter Set, id/size=0x990ce8/1048, name=H264 Picture Parameter Set, id/size=0x990ce9/12, name=H264 Scaling Matrix, id/size=0x990cea/480, name=H264 Slice Parameters, id/size=0x990ceb/896, name=H264 Decode Parameters, id/size=0x990cec/400 For instance, this is specially helpful when the ioctl fails. Consider the following example: v4l2-ctrls: prepare_ext_ctrls: video1: pointer control id 0x990cec size too small, 400 bytes but 784 bytes needed v4l2-ctrls: try_set_ext_ctrls: video1: video1: try_set_ext_ctrls_common failed (-14) video1: VIDIOC_S_EXT_CTRLS: error -14: which=0xf010000, count=5, error_idx=5, request_fd=39, name=H264 Sequence Parameter Set, id/size=0x990ce8/1048, name=H264 Picture Parameter Set, id/size=0x990ce9/12, name=H264 Scaling Matrix, id/size=0x990cea/480, name=H264 Slice Parameters, id/size=0x990ceb/896, name=H264 Decode Parameters, id/size=0x990cec/400 Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 02bfef0da76d..a556880f225a 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -582,7 +582,10 @@ static void v4l_print_querymenu(const void *arg, bool write_only) static void v4l_print_control(const void *arg, bool write_only) { const struct v4l2_control *p = arg; + const char *name = v4l2_ctrl_get_name(p->id); + if (name) + pr_cont("name=%s, ", name); pr_cont("id=0x%x, value=%d\n", p->id, p->value); } @@ -594,12 +597,15 @@ static void v4l_print_ext_controls(const void *arg, bool write_only) pr_cont("which=0x%x, count=%d, error_idx=%d, request_fd=%d", p->which, p->count, p->error_idx, p->request_fd); for (i = 0; i < p->count; i++) { + unsigned int id = p->controls[i].id; + const char *name = v4l2_ctrl_get_name(id); + + if (name) + pr_cont(", name=%s", name); if (!p->controls[i].size) - pr_cont(", id/val=0x%x/0x%x", - p->controls[i].id, p->controls[i].value); + pr_cont(", id/val=0x%x/0x%x", id, p->controls[i].value); else - pr_cont(", id/size=0x%x/%u", - p->controls[i].id, p->controls[i].size); + pr_cont(", id/size=0x%x/%u", id, p->controls[i].size); } pr_cont("\n"); } -- cgit v1.2.3 From 77d7ceb1a14616af8877c77674c8a17ed45fce85 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 1 Jul 2020 15:56:52 +0200 Subject: media: gspca: remove redundant initialization of variable status The variable status is being initialized with a value that is never read and it is being updated later with a new value. The initialization is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/sn9c2028.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/gspca/sn9c2028.c b/drivers/media/usb/gspca/sn9c2028.c index aff01b753853..dbd1d6da37f1 100644 --- a/drivers/media/usb/gspca/sn9c2028.c +++ b/drivers/media/usb/gspca/sn9c2028.c @@ -215,7 +215,7 @@ static int sd_config(struct gspca_dev *gspca_dev, /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { - int status = -1; + int status; sn9c2028_read1(gspca_dev); sn9c2028_read1(gspca_dev); -- cgit v1.2.3 From de26c90fc40d7d32a6f3f71babad2547ab3b7abb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 7 Jul 2020 15:10:15 +0200 Subject: sunxi-csi: fill in bus_info of media device This fixes a v4l2-compliance warning: Required ioctls: warn: v4l2-test-media.cpp(52): empty bus_info test MEDIA_IOC_DEVICE_INFO: OK Signed-off-by: Hans Verkuil Acked-by: Maxime Ripard Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c | 2 ++ drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c index eff34ded6305..5319eb1ab309 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c @@ -191,6 +191,8 @@ static int sun4i_csi_probe(struct platform_device *pdev) strscpy(csi->mdev.model, "Allwinner Video Capture Device", sizeof(csi->mdev.model)); csi->mdev.hw_revision = 0; + snprintf(csi->mdev.bus_info, sizeof(csi->mdev.bus_info), "platform:%s", + dev_name(csi->dev)); media_device_init(&csi->mdev); csi->v4l.mdev = &csi->mdev; diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c index 055eb0b8e396..28e89340fed9 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c @@ -733,6 +733,8 @@ static int sun6i_csi_v4l2_init(struct sun6i_csi *csi) strscpy(csi->media_dev.model, "Allwinner Video Capture Device", sizeof(csi->media_dev.model)); csi->media_dev.hw_revision = 0; + snprintf(csi->media_dev.bus_info, sizeof(csi->media_dev.bus_info), + "platform:%s", dev_name(csi->dev)); media_device_init(&csi->media_dev); v4l2_async_notifier_init(&csi->notifier); -- cgit v1.2.3 From 71ecc5356d227072c7c31e44f6774d461fd4865f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 7 Jul 2020 15:32:59 +0200 Subject: sun4i-csi: drop read/write, enable VB2_DMABUF For no obvious reason VB2_DMABUF was not enabled. Add this. Also both read and write file ops were set, but this was not enabled in the vb2 io_modes or in the device_caps capabilities. Drop this. Signed-off-by: Hans Verkuil Acked-by: Maxime Ripard Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c | 2 +- drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c index 78fa1c535ac6..3278746246aa 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c @@ -413,7 +413,7 @@ int sun4i_csi_dma_register(struct sun4i_csi *csi, int irq) q->min_buffers_needed = 3; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - q->io_modes = VB2_MMAP; + q->io_modes = VB2_MMAP | VB2_DMABUF; q->lock = &csi->lock; q->drv_priv = csi; q->buf_struct_size = sizeof(struct sun4i_csi_buffer); diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c index 1721e5aee9c6..06bf0d170904 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c @@ -256,8 +256,6 @@ static const struct v4l2_file_operations sun4i_csi_fops = { .open = sun4i_csi_open, .release = sun4i_csi_release, .unlocked_ioctl = video_ioctl2, - .read = vb2_fop_read, - .write = vb2_fop_write, .poll = vb2_fop_poll, .mmap = vb2_fop_mmap, }; -- cgit v1.2.3 From 503ebad4b5d7883ab8476d95f0e2aa77bc56f91e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 7 Jul 2020 15:36:47 +0200 Subject: sun4i-csi: call _vb2_fop_release instead of v4l2_fh_release This driver didn't call _vb2_fop_release in the sun4i_csi_release function. Without that call the vb2 queue is not properly canceled and buffers may not be freed. Signed-off-by: Hans Verkuil Acked-by: Maxime Ripard Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c index 06bf0d170904..8f4e254b6a41 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c @@ -242,7 +242,8 @@ static int sun4i_csi_release(struct file *file) mutex_lock(&csi->lock); - v4l2_fh_release(file); + _vb2_fop_release(file, NULL); + v4l2_pipeline_pm_put(&csi->vdev.entity); pm_runtime_put(csi->dev); -- cgit v1.2.3 From cf293a4f0e1a94c9a902d54879b189d459113c63 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 8 Jul 2020 08:24:20 +0200 Subject: media: solo6x10: replace http references with https Replace HTTP links with HTTPS ones: SOFTLOGIC 6x10 MPEG CODEC Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Ismael Luceno Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/solo6x10/solo6x10-core.c | 2 +- drivers/media/pci/solo6x10/solo6x10-disp.c | 2 +- drivers/media/pci/solo6x10/solo6x10-eeprom.c | 2 +- drivers/media/pci/solo6x10/solo6x10-enc.c | 2 +- drivers/media/pci/solo6x10/solo6x10-g723.c | 2 +- drivers/media/pci/solo6x10/solo6x10-gpio.c | 2 +- drivers/media/pci/solo6x10/solo6x10-i2c.c | 2 +- drivers/media/pci/solo6x10/solo6x10-jpeg.h | 2 +- drivers/media/pci/solo6x10/solo6x10-offsets.h | 2 +- drivers/media/pci/solo6x10/solo6x10-p2m.c | 2 +- drivers/media/pci/solo6x10/solo6x10-regs.h | 2 +- drivers/media/pci/solo6x10/solo6x10-tw28.c | 2 +- drivers/media/pci/solo6x10/solo6x10-tw28.h | 2 +- drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c | 2 +- drivers/media/pci/solo6x10/solo6x10-v4l2.c | 2 +- drivers/media/pci/solo6x10/solo6x10.h | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/solo6x10/solo6x10-core.c b/drivers/media/pci/solo6x10/solo6x10-core.c index 6e1ba4846ea4..c6e0090f27e8 100644 --- a/drivers/media/pci/solo6x10/solo6x10-core.c +++ b/drivers/media/pci/solo6x10/solo6x10-core.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-disp.c b/drivers/media/pci/solo6x10/solo6x10-disp.c index 4e28bf927de5..ad98ca7fb98b 100644 --- a/drivers/media/pci/solo6x10/solo6x10-disp.c +++ b/drivers/media/pci/solo6x10/solo6x10-disp.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-eeprom.c b/drivers/media/pci/solo6x10/solo6x10-eeprom.c index 9aba64395a6b..0d864b8ca0ab 100644 --- a/drivers/media/pci/solo6x10/solo6x10-eeprom.c +++ b/drivers/media/pci/solo6x10/solo6x10-eeprom.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-enc.c b/drivers/media/pci/solo6x10/solo6x10-enc.c index a9c56897f7bc..14a1d51cfad4 100644 --- a/drivers/media/pci/solo6x10/solo6x10-enc.c +++ b/drivers/media/pci/solo6x10/solo6x10-enc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-g723.c b/drivers/media/pci/solo6x10/solo6x10-g723.c index d6d16e8fd997..906ce86437ae 100644 --- a/drivers/media/pci/solo6x10/solo6x10-g723.c +++ b/drivers/media/pci/solo6x10/solo6x10-g723.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-gpio.c b/drivers/media/pci/solo6x10/solo6x10-gpio.c index 526d67cf9942..084c30760e45 100644 --- a/drivers/media/pci/solo6x10/solo6x10-gpio.c +++ b/drivers/media/pci/solo6x10/solo6x10-gpio.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-i2c.c b/drivers/media/pci/solo6x10/solo6x10-i2c.c index df1e3f2e9c05..f86f12fa6350 100644 --- a/drivers/media/pci/solo6x10/solo6x10-i2c.c +++ b/drivers/media/pci/solo6x10/solo6x10-i2c.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-jpeg.h b/drivers/media/pci/solo6x10/solo6x10-jpeg.h index e35aad16ad33..e212f4828c09 100644 --- a/drivers/media/pci/solo6x10/solo6x10-jpeg.h +++ b/drivers/media/pci/solo6x10/solo6x10-jpeg.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-offsets.h b/drivers/media/pci/solo6x10/solo6x10-offsets.h index e3ae6a02dbb9..f414ee1316f2 100644 --- a/drivers/media/pci/solo6x10/solo6x10-offsets.h +++ b/drivers/media/pci/solo6x10/solo6x10-offsets.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-p2m.c b/drivers/media/pci/solo6x10/solo6x10-p2m.c index e2816e2629f8..db2afc6a5fcb 100644 --- a/drivers/media/pci/solo6x10/solo6x10-p2m.c +++ b/drivers/media/pci/solo6x10/solo6x10-p2m.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-regs.h b/drivers/media/pci/solo6x10/solo6x10-regs.h index 804505d01b25..12e0ac190416 100644 --- a/drivers/media/pci/solo6x10/solo6x10-regs.h +++ b/drivers/media/pci/solo6x10/solo6x10-regs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-tw28.c b/drivers/media/pci/solo6x10/solo6x10-tw28.c index 126cd1b01266..1b7c22a9bc94 100644 --- a/drivers/media/pci/solo6x10/solo6x10-tw28.c +++ b/drivers/media/pci/solo6x10/solo6x10-tw28.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-tw28.h b/drivers/media/pci/solo6x10/solo6x10-tw28.h index edbad194d31e..4a8ede3139a8 100644 --- a/drivers/media/pci/solo6x10/solo6x10-tw28.h +++ b/drivers/media/pci/solo6x10/solo6x10-tw28.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c index cbf85231b708..3cf7bd6186aa 100644 --- a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c +++ b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2.c b/drivers/media/pci/solo6x10/solo6x10-v4l2.c index 54434f3c428d..24ef0c446bef 100644 --- a/drivers/media/pci/solo6x10/solo6x10-v4l2.c +++ b/drivers/media/pci/solo6x10/solo6x10-v4l2.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10.h b/drivers/media/pci/solo6x10/solo6x10.h index 9f2314688cec..126f6fb7b755 100644 --- a/drivers/media/pci/solo6x10/solo6x10.h +++ b/drivers/media/pci/solo6x10/solo6x10.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins -- cgit v1.2.3 From bb79974cdf953dc49ef653c59fda4daf1acc32b9 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 9 Jul 2020 09:28:14 +0200 Subject: media: davinci: replace http references with https TI DAVINCI SERIES MEDIA DRIVER: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Reviewed-by: Kieran Bingham Reviewed-by: Lad Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpbe_display.c | 2 +- drivers/media/platform/davinci/vpif.c | 2 +- drivers/media/platform/davinci/vpif.h | 2 +- drivers/media/platform/davinci/vpif_display.c | 2 +- drivers/media/platform/davinci/vpif_display.h | 2 +- include/media/davinci/vpbe_display.h | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 7ab13eb7527d..d19bad997f30 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ */ #include #include diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c index f28c1b27eac6..5e67994e62cc 100644 --- a/drivers/media/platform/davinci/vpif.c +++ b/drivers/media/platform/davinci/vpif.c @@ -5,7 +5,7 @@ * The hardware supports SDTV, HDTV formats, raw data capture. * Currently, the driver supports NTSC and PAL standards. * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/drivers/media/platform/davinci/vpif.h b/drivers/media/platform/davinci/vpif.h index 2466c7c77deb..c6d1d890478a 100644 --- a/drivers/media/platform/davinci/vpif.h +++ b/drivers/media/platform/davinci/vpif.h @@ -1,7 +1,7 @@ /* * VPIF header file * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index 7d55fd45240e..46afc029138f 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -2,7 +2,7 @@ * vpif-display - VPIF display driver * Display driver for TI DaVinci VPIF * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/ * Copyright (C) 2014 Lad, Prabhakar * * This program is free software; you can redistribute it and/or diff --git a/drivers/media/platform/davinci/vpif_display.h b/drivers/media/platform/davinci/vpif_display.h index af2765fdcea8..f731a65eefd6 100644 --- a/drivers/media/platform/davinci/vpif_display.h +++ b/drivers/media/platform/davinci/vpif_display.h @@ -1,7 +1,7 @@ /* * VPIF display header file * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/include/media/davinci/vpbe_display.h b/include/media/davinci/vpbe_display.h index 56d05a855140..6d2a93740130 100644 --- a/include/media/davinci/vpbe_display.h +++ b/include/media/davinci/vpbe_display.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ */ #ifndef VPBE_DISPLAY_H #define VPBE_DISPLAY_H -- cgit v1.2.3 From 2e7ee8bb24a72b924ef78908737d81f81cf93053 Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Thu, 9 Jul 2020 14:06:45 +0200 Subject: media: atmel: atmel-isc: rework component offsets The component offsets were computed in a negative way: they were subtracted from the actual color component value. So, a higher offset was reducing the component value. This is not really desirable, as the offset is a 2's complements number with 1 bit for sign and 12 value bits, so we would like to be able to also add to the component, not only subtract. The reported number in v4l2 is fine, a range from -4095 to +4095. However when configuring a negative value for the offset, this would in fact not function, because with the old code, the number was subtracted from the max value. By setting something negative, it was overflowing in fact. Reworked the component offsets by placing the real value as the v4l2 ctrls. Now, the values are the real number that is added or subtracted from the component. The negative values received from v4l2 are already in 2's complements, so there is no need for conversion. This actually simplifies a lot the computation procedure, eliminating the need for the macros that convert from v4l2 values to ISC values and viceversa. Also the ZERO_VAL is eliminated, as 0 is now 0, as it's supposed to be. Example after this change: # v4l2-ctl --set-ctrl=red_component_offset=-150 -L User Controls brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=16 value=20 flags=slider white_balance_automatic 0x0098090c (bool) : default=1 value=0 flags=update do_white_balance 0x0098090d (button) : flags=inactive, write-only, execute-on-write gamma 0x00980910 (int) : min=0 max=3 step=1 default=3 value=3 flags=slider red_component_gain 0x009819c0 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider blue_component_gain 0x009819c1 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider green_red_component_gain 0x009819c2 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider green_blue_component_gain 0x009819c3 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider red_component_offset 0x009819c4 (int) : min=-4095 max=4095 step=1 default=0 value=-150 flags=slider blue_component_offset 0x009819c5 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider green_red_component_offset 0x009819c6 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider green_blue_component_offset 0x009819c7 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider The auto white balance algorithm is unchanged, but the obtained value to 'subtract' is now converted to negative and saved as a v4l2 control and displayed properly. Signed-off-by: Eugen Hristev Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/atmel/atmel-isc-base.c | 60 +++++++++++---------------- drivers/media/platform/atmel/atmel-isc-regs.h | 2 - drivers/media/platform/atmel/atmel-isc.h | 2 +- 3 files changed, 25 insertions(+), 39 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/atmel/atmel-isc-base.c b/drivers/media/platform/atmel/atmel-isc-base.c index a6e9797a0ec9..fe3ec8d0eaee 100644 --- a/drivers/media/platform/atmel/atmel-isc-base.c +++ b/drivers/media/platform/atmel/atmel-isc-base.c @@ -225,9 +225,6 @@ const u32 isc_gamma_table[GAMMA_MAX + 1][GAMMA_ENTRIES] = { (((mbus_code) == MEDIA_BUS_FMT_Y10_1X10) | \ (((mbus_code) == MEDIA_BUS_FMT_Y8_1X8))) -#define ISC_CTRL_ISC_TO_V4L2(x) ((x) == ISC_WB_O_ZERO_VAL ? 0 : (x)) -#define ISC_CTRL_V4L2_TO_ISC(x) ((x) ? (x) : ISC_WB_O_ZERO_VAL) - static inline void isc_update_v4l2_ctrls(struct isc_device *isc) { struct isc_ctrls *ctrls = &isc->ctrls; @@ -238,14 +235,10 @@ static inline void isc_update_v4l2_ctrls(struct isc_device *isc) v4l2_ctrl_s_ctrl(isc->gr_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GR]); v4l2_ctrl_s_ctrl(isc->gb_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GB]); - v4l2_ctrl_s_ctrl(isc->r_off_ctrl, - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_R])); - v4l2_ctrl_s_ctrl(isc->b_off_ctrl, - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_B])); - v4l2_ctrl_s_ctrl(isc->gr_off_ctrl, - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GR])); - v4l2_ctrl_s_ctrl(isc->gb_off_ctrl, - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GB])); + v4l2_ctrl_s_ctrl(isc->r_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_R]); + v4l2_ctrl_s_ctrl(isc->b_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_B]); + v4l2_ctrl_s_ctrl(isc->gr_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GR]); + v4l2_ctrl_s_ctrl(isc->gb_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GB]); } static inline void isc_update_awb_ctrls(struct isc_device *isc) @@ -255,11 +248,11 @@ static inline void isc_update_awb_ctrls(struct isc_device *isc) /* In here we set our actual hw pipeline config */ regmap_write(isc->regmap, ISC_WB_O_RGR, - (ISC_WB_O_ZERO_VAL - (ctrls->offset[ISC_HIS_CFG_MODE_R])) | - ((ISC_WB_O_ZERO_VAL - ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16)); + ((ctrls->offset[ISC_HIS_CFG_MODE_R])) | + ((ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16)); regmap_write(isc->regmap, ISC_WB_O_BGB, - (ISC_WB_O_ZERO_VAL - (ctrls->offset[ISC_HIS_CFG_MODE_B])) | - ((ISC_WB_O_ZERO_VAL - ctrls->offset[ISC_HIS_CFG_MODE_GB]) << 16)); + ((ctrls->offset[ISC_HIS_CFG_MODE_B])) | + ((ctrls->offset[ISC_HIS_CFG_MODE_GB]) << 16)); regmap_write(isc->regmap, ISC_WB_G_RGR, ctrls->gain[ISC_HIS_CFG_MODE_R] | (ctrls->gain[ISC_HIS_CFG_MODE_GR] << 16)); @@ -275,12 +268,8 @@ static inline void isc_reset_awb_ctrls(struct isc_device *isc) for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) { /* gains have a fixed point at 9 decimals */ isc->ctrls.gain[c] = 1 << 9; - /* offsets are in 2's complements, the value - * will be substracted from ISC_WB_O_ZERO_VAL to obtain - * 2's complement of a value between 0 and - * ISC_WB_O_ZERO_VAL >> 1 - */ - isc->ctrls.offset[c] = ISC_WB_O_ZERO_VAL; + /* offsets are in 2's complements */ + isc->ctrls.offset[c] = 0; } } @@ -1774,9 +1763,12 @@ static void isc_wb_update(struct isc_ctrls *ctrls) */ ctrls->offset[c] = (offset[c] - 1) << 3; - /* the offset is then taken and converted to 2's complements */ - if (!ctrls->offset[c]) - ctrls->offset[c] = ISC_WB_O_ZERO_VAL; + /* + * the offset is then taken and converted to 2's complements, + * and must be negative, as we subtract this value from the + * color components + */ + ctrls->offset[c] = -ctrls->offset[c]; /* * the stretch gain is the total number of histogram bins @@ -1938,17 +1930,13 @@ static int isc_s_awb_ctrl(struct v4l2_ctrl *ctrl) ctrls->gain[ISC_HIS_CFG_MODE_GB] = isc->gb_gain_ctrl->val; if (ctrl->cluster[ISC_CTRL_R_OFF]->is_new) - ctrls->offset[ISC_HIS_CFG_MODE_R] = - ISC_CTRL_V4L2_TO_ISC(isc->r_off_ctrl->val); + ctrls->offset[ISC_HIS_CFG_MODE_R] = isc->r_off_ctrl->val; if (ctrl->cluster[ISC_CTRL_B_OFF]->is_new) - ctrls->offset[ISC_HIS_CFG_MODE_B] = - ISC_CTRL_V4L2_TO_ISC(isc->b_off_ctrl->val); + ctrls->offset[ISC_HIS_CFG_MODE_B] = isc->b_off_ctrl->val; if (ctrl->cluster[ISC_CTRL_GR_OFF]->is_new) - ctrls->offset[ISC_HIS_CFG_MODE_GR] = - ISC_CTRL_V4L2_TO_ISC(isc->gr_off_ctrl->val); + ctrls->offset[ISC_HIS_CFG_MODE_GR] = isc->gr_off_ctrl->val; if (ctrl->cluster[ISC_CTRL_GB_OFF]->is_new) - ctrls->offset[ISC_HIS_CFG_MODE_GB] = - ISC_CTRL_V4L2_TO_ISC(isc->gb_off_ctrl->val); + ctrls->offset[ISC_HIS_CFG_MODE_GB] = isc->gb_off_ctrl->val; isc_update_awb_ctrls(isc); @@ -2010,13 +1998,13 @@ static int isc_g_volatile_awb_ctrl(struct v4l2_ctrl *ctrl) ctrls->gain[ISC_HIS_CFG_MODE_GB]; ctrl->cluster[ISC_CTRL_R_OFF]->val = - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_R]); + ctrls->offset[ISC_HIS_CFG_MODE_R]; ctrl->cluster[ISC_CTRL_B_OFF]->val = - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_B]); + ctrls->offset[ISC_HIS_CFG_MODE_B]; ctrl->cluster[ISC_CTRL_GR_OFF]->val = - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GR]); + ctrls->offset[ISC_HIS_CFG_MODE_GR]; ctrl->cluster[ISC_CTRL_GB_OFF]->val = - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GB]); + ctrls->offset[ISC_HIS_CFG_MODE_GB]; break; } return 0; diff --git a/drivers/media/platform/atmel/atmel-isc-regs.h b/drivers/media/platform/atmel/atmel-isc-regs.h index c1283fb21bf6..f1e160ed4351 100644 --- a/drivers/media/platform/atmel/atmel-isc-regs.h +++ b/drivers/media/platform/atmel/atmel-isc-regs.h @@ -108,8 +108,6 @@ /* ISC White Balance Gain for B, GB Register */ #define ISC_WB_G_BGB 0x0000006c -#define ISC_WB_O_ZERO_VAL (1 << 13) - /* ISC Color Filter Array Control Register */ #define ISC_CFA_CTRL 0x00000070 diff --git a/drivers/media/platform/atmel/atmel-isc.h b/drivers/media/platform/atmel/atmel-isc.h index fc56a745c7d1..24b784b893d6 100644 --- a/drivers/media/platform/atmel/atmel-isc.h +++ b/drivers/media/platform/atmel/atmel-isc.h @@ -133,7 +133,7 @@ struct isc_ctrls { /* one for each component : GR, R, GB, B */ u32 gain[HIST_BAYER]; - u32 offset[HIST_BAYER]; + s32 offset[HIST_BAYER]; u32 hist_entry[HIST_ENTRIES]; u32 hist_count[HIST_BAYER]; -- cgit v1.2.3 From 3bdf27a33cc4f4806ab6ced841aaffed6625544f Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 9 Jul 2020 22:29:17 +0200 Subject: media: gspca: replace http references with https ones GSPCA USB WEBCAM DRIVER: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/ov534.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c index 185c1f10fb30..9a11158f38da 100644 --- a/drivers/media/usb/gspca/ov534.c +++ b/drivers/media/usb/gspca/ov534.c @@ -14,7 +14,7 @@ * PS3 Eye camera - brightness, contrast, awb, agc, aec controls * added by Max Thrun * PS3 Eye camera - FPS range extended by Joseph Howse - * http://nummist.com + * https://nummist.com */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -- cgit v1.2.3 From d6129d36b305e9943bc6af0e4f0f7f6a77ded9b0 Mon Sep 17 00:00:00 2001 From: Flavio Suligoi Date: Tue, 16 Jun 2020 13:52:55 +0200 Subject: media: pci: dt3155: fix spelling mistake Fix typo: "TRIGER" --> "TRIGGER" Signed-off-by: Flavio Suligoi Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/dt3155/dt3155.c | 2 +- drivers/media/pci/dt3155/dt3155.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/dt3155/dt3155.c b/drivers/media/pci/dt3155/dt3155.c index 82581aa5a2a3..ef8d5c9cfffe 100644 --- a/drivers/media/pci/dt3155/dt3155.c +++ b/drivers/media/pci/dt3155/dt3155.c @@ -426,7 +426,7 @@ static int dt3155_init_board(struct dt3155_priv *pd) iowrite32(FIFO_EN | SRST, pd->regs + CSR1); iowrite32(0xEEEEEE01, pd->regs + EVEN_PIXEL_FMT); iowrite32(0xEEEEEE01, pd->regs + ODD_PIXEL_FMT); - iowrite32(0x00000020, pd->regs + FIFO_TRIGER); + iowrite32(0x00000020, pd->regs + FIFO_TRIGGER); iowrite32(0x00000103, pd->regs + XFER_MODE); iowrite32(0, pd->regs + RETRY_WAIT_CNT); iowrite32(0, pd->regs + INT_CSR); diff --git a/drivers/media/pci/dt3155/dt3155.h b/drivers/media/pci/dt3155/dt3155.h index 43d32b8d5feb..c9ce79cb5566 100644 --- a/drivers/media/pci/dt3155/dt3155.h +++ b/drivers/media/pci/dt3155/dt3155.h @@ -31,7 +31,7 @@ #define ODD_DMA_STRIDE 0x24 #define EVEN_PIXEL_FMT 0x30 #define ODD_PIXEL_FMT 0x34 -#define FIFO_TRIGER 0x38 +#define FIFO_TRIGGER 0x38 #define XFER_MODE 0x3C #define CSR1 0x40 #define RETRY_WAIT_CNT 0x44 -- cgit v1.2.3 From b3ab1c6058fad8cd5726f24e9ed9053e43bb2af4 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 24 Jun 2020 21:28:00 +0200 Subject: media: Add V4L2_TYPE_IS_CAPTURE helper It's all too easy to get confused by the V4L2_TYPE_IS_OUTPUT macro, when it's used as !V4L2_TYPE_IS_OUTPUT. Reduce the risk of confusion with macro to explicitly check for the CAPTURE queue type case. This change does not affect functionality, and it's only intended to make the code more readable. Suggested-by: Nicolas Dufresne Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil [hverkuil-cisco@xs4all.nl: checkpatch: align with parenthesis] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-v4l2.c | 4 ++-- drivers/media/platform/exynos-gsc/gsc-core.c | 2 +- drivers/media/platform/exynos-gsc/gsc-m2m.c | 2 +- drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c | 2 +- drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c | 7 +++---- drivers/media/platform/rcar_jpu.c | 2 +- drivers/media/platform/sti/hva/hva-v4l2.c | 2 +- drivers/media/platform/ti-vpe/vpe.c | 2 +- drivers/media/test-drivers/vicodec/vicodec-core.c | 6 +++--- drivers/media/v4l2-core/v4l2-mem2mem.c | 6 +++--- drivers/staging/media/hantro/hantro_v4l2.c | 2 +- drivers/staging/media/rkvdec/rkvdec.c | 2 +- include/uapi/linux/videodev2.h | 2 ++ 13 files changed, 21 insertions(+), 20 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 57aa183bd198..30caad27281e 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -97,7 +97,7 @@ static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b) unsigned int bytesused; unsigned int plane; - if (!V4L2_TYPE_IS_OUTPUT(b->type)) + if (V4L2_TYPE_IS_CAPTURE(b->type)) return 0; if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { @@ -311,7 +311,7 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b /* Zero flags that we handle */ vbuf->flags = b->flags & ~V4L2_BUFFER_MASK_FLAGS; - if (!vb->vb2_queue->copy_timestamp || !V4L2_TYPE_IS_OUTPUT(b->type)) { + if (!vb->vb2_queue->copy_timestamp || V4L2_TYPE_IS_CAPTURE(b->type)) { /* * Non-COPY timestamps and non-OUTPUT queues will get * their timestamp and timestamp source flags from the diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index f6650b45bc3d..9f41c2e7097a 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -577,7 +577,7 @@ int gsc_try_selection(struct gsc_ctx *ctx, struct v4l2_selection *s) v4l_bound_align_image(&tmp_w, min_w, max_w, mod_x, &tmp_h, min_h, max_h, mod_y, 0); - if (!V4L2_TYPE_IS_OUTPUT(s->type) && + if (V4L2_TYPE_IS_CAPTURE(s->type) && (ctx->gsc_ctrls.rotate->val == 90 || ctx->gsc_ctrls.rotate->val == 270)) gsc_check_crop_change(tmp_h, tmp_w, diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index e2c162635f72..27a3c92c73bc 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -255,7 +255,7 @@ static int gsc_m2m_buf_prepare(struct vb2_buffer *vb) if (IS_ERR(frame)) return PTR_ERR(frame); - if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) { for (i = 0; i < frame->fmt->num_planes; i++) vb2_set_plane_payload(vb, i, frame->payload[i]); } diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c index f82a81a3bdee..61fed1e35a00 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c @@ -731,7 +731,7 @@ static void mtk_jpeg_stop_streaming(struct vb2_queue *q) * subsampling. Update capture queue when the stream is off. */ if (ctx->state == MTK_JPEG_SOURCE_CHANGE && - !V4L2_TYPE_IS_OUTPUT(q->type)) { + V4L2_TYPE_IS_CAPTURE(q->type)) { struct mtk_jpeg_src_buf *src_buf; vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c index bb9caaf513bc..724c7333b6e5 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c @@ -193,7 +193,7 @@ static const struct mtk_mdp_fmt *mtk_mdp_try_fmt_mplane(struct mtk_mdp_ctx *ctx, pix_mp->field = V4L2_FIELD_NONE; pix_mp->pixelformat = fmt->pixelformat; - if (!V4L2_TYPE_IS_OUTPUT(f->type)) { + if (V4L2_TYPE_IS_CAPTURE(f->type)) { pix_mp->colorspace = ctx->colorspace; pix_mp->xfer_func = ctx->xfer_func; pix_mp->ycbcr_enc = ctx->ycbcr_enc; @@ -327,9 +327,8 @@ static int mtk_mdp_try_crop(struct mtk_mdp_ctx *ctx, u32 type, mtk_mdp_bound_align_image(&new_w, min_w, max_w, align_w, &new_h, min_h, max_h, align_h); - if (!V4L2_TYPE_IS_OUTPUT(type) && - (ctx->ctrls.rotate->val == 90 || - ctx->ctrls.rotate->val == 270)) + if (V4L2_TYPE_IS_CAPTURE(type) && + (ctx->ctrls.rotate->val == 90 || ctx->ctrls.rotate->val == 270)) mtk_mdp_check_crop_change(new_h, new_w, &r->width, &r->height); else diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c index 5250a14324e9..9b99ff368698 100644 --- a/drivers/media/platform/rcar_jpu.c +++ b/drivers/media/platform/rcar_jpu.c @@ -1066,7 +1066,7 @@ static int jpu_buf_prepare(struct vb2_buffer *vb) } /* decoder capture queue */ - if (!ctx->encoder && !V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) + if (!ctx->encoder && V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) vb2_set_plane_payload(vb, i, size); } diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/sti/hva/hva-v4l2.c index 197b99d8fd9c..bb34d6997d99 100644 --- a/drivers/media/platform/sti/hva/hva-v4l2.c +++ b/drivers/media/platform/sti/hva/hva-v4l2.c @@ -1087,7 +1087,7 @@ static void hva_stop_streaming(struct vb2_queue *vq) if ((V4L2_TYPE_IS_OUTPUT(vq->type) && vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q)) || - (!V4L2_TYPE_IS_OUTPUT(vq->type) && + (V4L2_TYPE_IS_CAPTURE(vq->type) && vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q))) { dev_dbg(dev, "%s %s out=%d cap=%d\n", ctx->name, to_type_str(vq->type), diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index cff2fcd6d812..346f8212791c 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -1576,7 +1576,7 @@ static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f) *f = q_data->format; - if (!V4L2_TYPE_IS_OUTPUT(f->type)) { + if (V4L2_TYPE_IS_CAPTURE(f->type)) { struct vpe_q_data *s_q_data; struct v4l2_pix_format_mplane *spix; diff --git a/drivers/media/test-drivers/vicodec/vicodec-core.c b/drivers/media/test-drivers/vicodec/vicodec-core.c index e879290727ef..8941d73f6611 100644 --- a/drivers/media/test-drivers/vicodec/vicodec-core.c +++ b/drivers/media/test-drivers/vicodec/vicodec-core.c @@ -1442,7 +1442,7 @@ static void vicodec_buf_queue(struct vb2_buffer *vb) .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, }; - if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type) && + if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type) && vb2_is_streaming(vb->vb2_queue) && v4l2_m2m_dst_buf_is_last(ctx->fh.m2m_ctx)) { unsigned int i; @@ -1479,7 +1479,7 @@ static void vicodec_buf_queue(struct vb2_buffer *vb) * in the compressed stream */ if (ctx->is_stateless || ctx->is_enc || - !V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) { v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); return; } @@ -1574,7 +1574,7 @@ static int vicodec_start_streaming(struct vb2_queue *q, state->gop_cnt = 0; if ((V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) || - (!V4L2_TYPE_IS_OUTPUT(q->type) && ctx->is_enc)) + (V4L2_TYPE_IS_CAPTURE(q->type) && ctx->is_enc)) return 0; if (info->id == V4L2_PIX_FMT_FWHT || diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 62ac9424c92a..95a8f2dc5341 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -556,7 +556,7 @@ int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, ret = vb2_querybuf(vq, buf); /* Adjust MMAP memory offsets for the CAPTURE queue */ - if (buf->memory == V4L2_MEMORY_MMAP && !V4L2_TYPE_IS_OUTPUT(vq->type)) { + if (buf->memory == V4L2_MEMORY_MMAP && V4L2_TYPE_IS_CAPTURE(vq->type)) { if (V4L2_TYPE_IS_MULTIPLANAR(vq->type)) { for (i = 0; i < buf->length; ++i) buf->m.planes[i].m.mem_offset @@ -712,7 +712,7 @@ int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, int ret; vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); - if (!V4L2_TYPE_IS_OUTPUT(vq->type) && + if (V4L2_TYPE_IS_CAPTURE(vq->type) && (buf->flags & V4L2_BUF_FLAG_REQUEST_FD)) { dprintk("%s: requests cannot be used with capture buffers\n", __func__); @@ -729,7 +729,7 @@ int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, * buffer as DONE with LAST flag since it won't be queued on the * device. */ - if (!V4L2_TYPE_IS_OUTPUT(vq->type) && + if (V4L2_TYPE_IS_CAPTURE(vq->type) && vb2_is_streaming(vq) && !vb2_start_streaming_called(vq) && (v4l2_m2m_has_stopped(m2m_ctx) || v4l2_m2m_dst_buf_is_last(m2m_ctx))) v4l2_m2m_force_last_buf_done(m2m_ctx, vq); diff --git a/drivers/staging/media/hantro/hantro_v4l2.c b/drivers/staging/media/hantro/hantro_v4l2.c index f28a94e2fa93..63859e8a0923 100644 --- a/drivers/staging/media/hantro/hantro_v4l2.c +++ b/drivers/staging/media/hantro/hantro_v4l2.c @@ -237,7 +237,7 @@ static int hantro_try_fmt(const struct hantro_ctx *ctx, enum v4l2_buf_type type) { const struct hantro_fmt *fmt, *vpu_fmt; - bool capture = !V4L2_TYPE_IS_OUTPUT(type); + bool capture = V4L2_TYPE_IS_CAPTURE(type); bool coded; coded = capture == hantro_is_encoder_ctx(ctx); diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c index 225eeca73356..fd68671f0286 100644 --- a/drivers/staging/media/rkvdec/rkvdec.c +++ b/drivers/staging/media/rkvdec/rkvdec.c @@ -489,7 +489,7 @@ static int rkvdec_start_streaming(struct vb2_queue *q, unsigned int count) const struct rkvdec_coded_fmt_desc *desc; int ret; - if (!V4L2_TYPE_IS_OUTPUT(q->type)) + if (V4L2_TYPE_IS_CAPTURE(q->type)) return 0; desc = ctx->coded_fmt_desc; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 303805438814..c7b70ff53bc1 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -171,6 +171,8 @@ enum v4l2_buf_type { || (type) == V4L2_BUF_TYPE_SDR_OUTPUT \ || (type) == V4L2_BUF_TYPE_META_OUTPUT) +#define V4L2_TYPE_IS_CAPTURE(type) (!V4L2_TYPE_IS_OUTPUT(type)) + enum v4l2_tuner_type { V4L2_TUNER_RADIO = 1, V4L2_TUNER_ANALOG_TV = 2, -- cgit v1.2.3 From 94b9ce6870f9c90ac92505482689818b254312f7 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Thu, 25 Jun 2020 22:19:47 +0200 Subject: media: s5p-g2d: Fix a memory leak in an error handling path in 'g2d_probe()' Memory allocated with 'v4l2_m2m_init()' must be freed by a corresponding call to 'v4l2_m2m_release()' Also reorder the code at the end of the probe function so that 'video_register_device()' is called last. Update the error handling path accordingly. Fixes: 5ce60d790a24 ("[media] s5p-g2d: Add DT based discovery support") Fixes: 918847341af0 ("[media] v4l: add G2D driver for s5p device family") Signed-off-by: Christophe JAILLET Signed-off-by: Hans Verkuil [hverkuil-cisco@xs4all.nl: checkpatch: align with parenthesis] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-g2d/g2d.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 6932fd47071b..15bcb7f6e113 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -695,21 +695,13 @@ static int g2d_probe(struct platform_device *pdev) vfd->lock = &dev->mutex; vfd->v4l2_dev = &dev->v4l2_dev; vfd->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; - ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); - if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); - goto rel_vdev; - } - video_set_drvdata(vfd, dev); - dev->vfd = vfd; - v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n", - vfd->num); + platform_set_drvdata(pdev, dev); dev->m2m_dev = v4l2_m2m_init(&g2d_m2m_ops); if (IS_ERR(dev->m2m_dev)) { v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); ret = PTR_ERR(dev->m2m_dev); - goto unreg_video_dev; + goto rel_vdev; } def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3; @@ -717,14 +709,24 @@ static int g2d_probe(struct platform_device *pdev) of_id = of_match_node(exynos_g2d_match, pdev->dev.of_node); if (!of_id) { ret = -ENODEV; - goto unreg_video_dev; + goto free_m2m; } dev->variant = (struct g2d_variant *)of_id->data; + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + goto free_m2m; + } + video_set_drvdata(vfd, dev); + dev->vfd = vfd; + v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n", + vfd->num); + return 0; -unreg_video_dev: - video_unregister_device(dev->vfd); +free_m2m: + v4l2_m2m_release(dev->m2m_dev); rel_vdev: video_device_release(vfd); unreg_v4l2_dev: -- cgit v1.2.3 From 44d42520df8a67f47c1e47d934d6bcd38d87daac Mon Sep 17 00:00:00 2001 From: Florin Tudorache Date: Sun, 28 Jun 2020 11:24:47 +0200 Subject: gspca: First camera mode is skipped If a device supports a resolution in 2 different formats, mode[0] is skipped on first iteration and a different format is selected. Example: 320x240/YUYV isn't reachable for OV534/ov772x Signed-off-by: Florin Tudorache Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/gspca.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index 0566e00d6fea..c295f642d352 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -925,7 +925,7 @@ static int wxh_to_nearest_mode(struct gspca_dev *gspca_dev, { int i; - for (i = gspca_dev->cam.nmodes; --i > 0; ) { + for (i = gspca_dev->cam.nmodes; --i >= 0; ) { if (width >= gspca_dev->cam.cam_mode[i].width && height >= gspca_dev->cam.cam_mode[i].height && pixelformat == gspca_dev->cam.cam_mode[i].pixelformat) -- cgit v1.2.3 From 03395df3977eba11e2ee9893082aa63fb6bdfc68 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:22 +0200 Subject: media: ti-vpe: cal: Sort headers alphabetically Sorting headers alphabetically helps locating duplicates. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 9b18db7af6c3..19425248bd63 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -7,30 +7,31 @@ */ #include +#include #include #include #include +#include #include +#include +#include #include -#include #include -#include -#include #include +#include #include -#include -#include -#include #include #include #include #include #include -#include #include +#include +#include #include #include + #include "cal_regs.h" #define CAL_MODULE_NAME "cal" -- cgit v1.2.3 From 789026265985f3c8d5d2081690cfa24637b1a333 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:23 +0200 Subject: media: ti-vpe: cal: Avoid function forward declaration Move the cal_complete_ctx() function earlier in the file to avoid a foward declaration. There is no functional change. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 111 ++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 56 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 19425248bd63..88fed930ed50 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1852,7 +1852,61 @@ static const struct video_device cal_videodev = { * Initialization and module stuff * ------------------------------------------------------------------ */ -static int cal_complete_ctx(struct cal_ctx *ctx); + +static int cal_complete_ctx(struct cal_ctx *ctx) +{ + struct video_device *vfd; + struct vb2_queue *q; + int ret; + + ctx->timeperframe = tpf_default; + ctx->external_rate = 192000000; + + /* initialize locks */ + spin_lock_init(&ctx->slock); + mutex_init(&ctx->mutex); + + /* initialize queue */ + q = &ctx->vb_vidq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + q->drv_priv = ctx; + q->buf_struct_size = sizeof(struct cal_buffer); + q->ops = &cal_video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &ctx->mutex; + q->min_buffers_needed = 3; + q->dev = ctx->v4l2_dev.dev; + + ret = vb2_queue_init(q); + if (ret) + return ret; + + /* init video dma queues */ + INIT_LIST_HEAD(&ctx->vidq.active); + + vfd = &ctx->vdev; + *vfd = cal_videodev; + vfd->v4l2_dev = &ctx->v4l2_dev; + vfd->queue = q; + + /* + * Provide a mutex to v4l2 core. It will be used to protect + * all fops and v4l2 ioctls. + */ + vfd->lock = &ctx->mutex; + video_set_drvdata(vfd, ctx); + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr); + if (ret < 0) + return ret; + + v4l2_info(&ctx->v4l2_dev, "V4L2 device registered as %s\n", + video_device_node_name(vfd)); + + return 0; +} static int cal_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, @@ -1948,61 +2002,6 @@ static const struct v4l2_async_notifier_operations cal_async_ops = { .complete = cal_async_complete, }; -static int cal_complete_ctx(struct cal_ctx *ctx) -{ - struct video_device *vfd; - struct vb2_queue *q; - int ret; - - ctx->timeperframe = tpf_default; - ctx->external_rate = 192000000; - - /* initialize locks */ - spin_lock_init(&ctx->slock); - mutex_init(&ctx->mutex); - - /* initialize queue */ - q = &ctx->vb_vidq; - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; - q->drv_priv = ctx; - q->buf_struct_size = sizeof(struct cal_buffer); - q->ops = &cal_video_qops; - q->mem_ops = &vb2_dma_contig_memops; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->lock = &ctx->mutex; - q->min_buffers_needed = 3; - q->dev = ctx->v4l2_dev.dev; - - ret = vb2_queue_init(q); - if (ret) - return ret; - - /* init video dma queues */ - INIT_LIST_HEAD(&ctx->vidq.active); - - vfd = &ctx->vdev; - *vfd = cal_videodev; - vfd->v4l2_dev = &ctx->v4l2_dev; - vfd->queue = q; - - /* - * Provide a mutex to v4l2 core. It will be used to protect - * all fops and v4l2 ioctls. - */ - vfd->lock = &ctx->mutex; - video_set_drvdata(vfd, ctx); - - ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr); - if (ret < 0) - return ret; - - v4l2_info(&ctx->v4l2_dev, "V4L2 device registered as %s\n", - video_device_node_name(vfd)); - - return 0; -} - static struct device_node * of_get_next_port(const struct device_node *parent, struct device_node *prev) -- cgit v1.2.3 From cff3784dfa6cca01c3001e8eca60ae0098de4c54 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:24 +0200 Subject: media: ti-vpe: cal: Decouple CSI2 port and CPORT The CPORT mentioned in the CAL documentation has nothing to do with the CSI-2 ports. It's a remnant of CSI-3 support (or an attempt to achieve it), and now only serves as an arbitraty tag value used to associate data samples with contexts. Add a cport field to the cal_ctx structure and use it instead of the CSI-2 port number where a CPORT is required by the hardware. The value of the cport field is currently the same as the csi2_port field, so there's no functional change, but this will help reworking CPORT and CSI-2 port handling separately. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 88fed930ed50..201ef3585a48 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -409,6 +409,7 @@ struct cal_ctx { struct vb2_queue vb_vidq; unsigned int seq_count; unsigned int csi2_port; + unsigned int cport; unsigned int virtual_channel; /* Pointer pointing to current v4l2_buffer */ @@ -971,7 +972,7 @@ static void csi2_ctx_config(struct cal_ctx *ctx) u32 val; val = reg_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port)); - set_field(&val, ctx->csi2_port, CAL_CSI2_CTX_CPORT_MASK); + set_field(&val, ctx->cport, CAL_CSI2_CTX_CPORT_MASK); /* * DT type: MIPI CSI-2 Specs * 0x1: All - DT filter is disabled @@ -1036,7 +1037,7 @@ static void pix_proc_config(struct cal_ctx *ctx) set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK); set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK); set_field(&val, pack, CAL_PIX_PROC_PACK_MASK); - set_field(&val, ctx->csi2_port, CAL_PIX_PROC_CPORT_MASK); + set_field(&val, ctx->cport, CAL_PIX_PROC_CPORT_MASK); set_field(&val, 1, CAL_PIX_PROC_EN_MASK); reg_write(ctx->dev, CAL_PIX_PROC(ctx->csi2_port), val); ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->csi2_port, @@ -1049,7 +1050,7 @@ static void cal_wr_dma_config(struct cal_ctx *ctx, u32 val; val = reg_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port)); - set_field(&val, ctx->csi2_port, CAL_WR_DMA_CTRL_CPORT_MASK); + set_field(&val, ctx->cport, CAL_WR_DMA_CTRL_CPORT_MASK); set_field(&val, height, CAL_WR_DMA_CTRL_YSIZE_MASK); set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT, CAL_WR_DMA_CTRL_DTAG_MASK); @@ -2221,6 +2222,7 @@ static struct cal_ctx *cal_create_instance(struct cal_dev *dev, int inst) /* Store the instance id */ ctx->csi2_port = inst + 1; + ctx->cport = inst + 1; ret = of_cal_create_instance(ctx, inst); if (ret) { -- cgit v1.2.3 From 6442241630d1273033ae7f6665c4caec19671633 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:25 +0200 Subject: media: ti-vpe: cal: Index CSI-2 port starting at 0 The datasheet numbers the CSI-2 PHYs (external to the CAL) starting from 1, but the PPI inputs to the CAL starting from 0. Update the numbering scheme for csi2_port to match the datasheet, which also simplifies the driver by removing the need to constantly subtract 1 from the value. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 30 +++++++-------- drivers/media/platform/ti-vpe/cal_regs.h | 64 ++++++++++++++++---------------- 2 files changed, 47 insertions(+), 47 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 201ef3585a48..799c25f5aafd 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -472,7 +472,7 @@ static inline void set_field(u32 *valp, u32 field, u32 mask) static u32 cal_data_get_phy_max_lanes(struct cal_ctx *ctx) { struct cal_dev *dev = ctx->dev; - u32 phy_id = ctx->csi2_port - 1; + u32 phy_id = ctx->csi2_port; return dev->data->csi2_phy_core[phy_id].num_lanes; } @@ -557,7 +557,7 @@ static struct regmap *cal_get_camerarx_regmap(struct cal_dev *dev) static void camerarx_phy_enable(struct cal_ctx *ctx) { struct cal_csi2_phy *phy; - u32 phy_id = ctx->csi2_port - 1; + u32 phy_id = ctx->csi2_port; u32 max_lanes; phy = &ctx->dev->data->csi2_phy_core[phy_id]; @@ -574,7 +574,7 @@ static void camerarx_phy_enable(struct cal_ctx *ctx) static void camerarx_phy_disable(struct cal_ctx *ctx) { struct cal_csi2_phy *phy; - u32 phy_id = ctx->csi2_port - 1; + u32 phy_id = ctx->csi2_port; phy = &ctx->dev->data->csi2_phy_core[phy_id]; regmap_field_write(phy->fields[F_CTRLCLKEN], 0); @@ -712,7 +712,7 @@ static void enable_irqs(struct cal_ctx *ctx) set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(3), val); /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0xFF000000); + reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); } static void disable_irqs(struct cal_ctx *ctx) @@ -734,7 +734,7 @@ static void disable_irqs(struct cal_ctx *ctx) set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(3), val); /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0); + reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(0), 0); } static void csi2_cio_power(struct cal_ctx *ctx, bool enable) @@ -894,7 +894,7 @@ static void csi2_wait_for_phy(struct cal_ctx *ctx) csi2_wait_stop_state(ctx); ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x (Bit(31,28) should be set!)\n", - (ctx->csi2_port - 1), reg_read(ctx->cc, CAL_CSI2_PHY_REG1)); + ctx->csi2_port, reg_read(ctx->cc, CAL_CSI2_PHY_REG1)); } static void csi2_phy_deinit(struct cal_ctx *ctx) @@ -1139,7 +1139,7 @@ static void csi2_phy_config(struct cal_ctx *ctx) set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); - ctx_dbg(1, ctx, "CSI2_%d_REG0 = 0x%08x\n", (ctx->csi2_port - 1), reg0); + ctx_dbg(1, ctx, "CSI2_%d_REG0 = 0x%08x\n", ctx->csi2_port, reg0); reg_write(ctx->cc, CAL_CSI2_PHY_REG0, reg0); reg1 = reg_read(ctx->cc, CAL_CSI2_PHY_REG1); @@ -1148,7 +1148,7 @@ static void csi2_phy_config(struct cal_ctx *ctx) set_field(®1, TCLK_MISS, CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); - ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x\n", (ctx->csi2_port - 1), reg1); + ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x\n", ctx->csi2_port, reg1); reg_write(ctx->cc, CAL_CSI2_PHY_REG1, reg1); } @@ -1217,7 +1217,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) if (irqst1 & CAL_HL_IRQ_OCPO_ERR_MASK) dev_err_ratelimited(&dev->pdev->dev, "OCPO ERROR\n"); - for (i = 1; i <= 2; ++i) { + for (i = 0; i < 2; ++i) { if (irqst1 & CAL_HL_IRQ_CIO_MASK(i)) { u32 cio_stat = reg_read(dev, CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); @@ -1239,9 +1239,9 @@ static irqreturn_t cal_irq(int irq_cal, void *data) /* Clear Interrupt status */ reg_write(dev, CAL_HL_IRQSTATUS(2), irqst2); - for (i = 1; i <= 2; ++i) { + for (i = 0; i < 2; ++i) { if (isportirqset(irqst2, i)) { - ctx = dev->ctx[i - 1]; + ctx = dev->ctx[i]; spin_lock(&ctx->slock); ctx->dma_act = false; @@ -1262,9 +1262,9 @@ static irqreturn_t cal_irq(int irq_cal, void *data) /* Clear Interrupt status */ reg_write(dev, CAL_HL_IRQSTATUS(3), irqst3); - for (i = 1; i <= 2; ++i) { + for (i = 0; i < 2; ++i) { if (isportirqset(irqst3, i)) { - ctx = dev->ctx[i - 1]; + ctx = dev->ctx[i]; dma_q = &ctx->vidq; spin_lock(&ctx->slock); @@ -2221,8 +2221,8 @@ static struct cal_ctx *cal_create_instance(struct cal_dev *dev, int inst) ctx->cc = dev->cc[inst]; /* Store the instance id */ - ctx->csi2_port = inst + 1; - ctx->cport = inst + 1; + ctx->csi2_port = inst; + ctx->cport = inst; ret = of_cal_create_instance(ctx, inst); if (ret) { diff --git a/drivers/media/platform/ti-vpe/cal_regs.h b/drivers/media/platform/ti-vpe/cal_regs.h index ac54a2fe7bb6..56c8ccd0e75f 100644 --- a/drivers/media/platform/ti-vpe/cal_regs.h +++ b/drivers/media/platform/ti-vpe/cal_regs.h @@ -46,7 +46,7 @@ #define CAL_HL_IRQSTATUS(m) (0x24U + ((m-1) * 0x10U)) #define CAL_HL_IRQENABLE_SET(m) (0x28U + ((m-1) * 0x10U)) #define CAL_HL_IRQENABLE_CLR(m) (0x2cU + ((m-1) * 0x10U)) -#define CAL_PIX_PROC(m) (0xc0U + ((m-1) * 0x4U)) +#define CAL_PIX_PROC(m) (0xc0U + (m) * 0x4U) #define CAL_CTRL 0x100 #define CAL_CTRL1 0x104 #define CAL_LINE_NUMBER_EVT 0x108 @@ -62,34 +62,34 @@ #define CAL_RD_DMA_INIT_ADDR 0x154 #define CAL_RD_DMA_INIT_OFST 0x168 #define CAL_RD_DMA_CTRL2 0x16c -#define CAL_WR_DMA_CTRL(m) (0x200U + ((m-1) * 0x10U)) -#define CAL_WR_DMA_ADDR(m) (0x204U + ((m-1) * 0x10U)) -#define CAL_WR_DMA_OFST(m) (0x208U + ((m-1) * 0x10U)) -#define CAL_WR_DMA_XSIZE(m) (0x20cU + ((m-1) * 0x10U)) -#define CAL_CSI2_PPI_CTRL(m) (0x300U + ((m-1) * 0x80U)) -#define CAL_CSI2_COMPLEXIO_CFG(m) (0x304U + ((m-1) * 0x80U)) -#define CAL_CSI2_COMPLEXIO_IRQSTATUS(m) (0x308U + ((m-1) * 0x80U)) -#define CAL_CSI2_SHORT_PACKET(m) (0x30cU + ((m-1) * 0x80U)) -#define CAL_CSI2_COMPLEXIO_IRQENABLE(m) (0x310U + ((m-1) * 0x80U)) -#define CAL_CSI2_TIMING(m) (0x314U + ((m-1) * 0x80U)) -#define CAL_CSI2_VC_IRQENABLE(m) (0x318U + ((m-1) * 0x80U)) -#define CAL_CSI2_VC_IRQSTATUS(m) (0x328U + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX0(m) (0x330U + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX1(m) (0x334U + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX2(m) (0x338U + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX3(m) (0x33cU + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX4(m) (0x340U + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX5(m) (0x344U + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX6(m) (0x348U + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX7(m) (0x34cU + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS0(m) (0x350U + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS1(m) (0x354U + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS2(m) (0x358U + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS3(m) (0x35cU + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS4(m) (0x360U + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS5(m) (0x364U + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS6(m) (0x368U + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS7(m) (0x36cU + ((m-1) * 0x80U)) +#define CAL_WR_DMA_CTRL(m) (0x200U + (m) * 0x10U) +#define CAL_WR_DMA_ADDR(m) (0x204U + (m) * 0x10U) +#define CAL_WR_DMA_OFST(m) (0x208U + (m) * 0x10U) +#define CAL_WR_DMA_XSIZE(m) (0x20cU + (m) * 0x10U) +#define CAL_CSI2_PPI_CTRL(m) (0x300U + (m) * 0x80U) +#define CAL_CSI2_COMPLEXIO_CFG(m) (0x304U + (m) * 0x80U) +#define CAL_CSI2_COMPLEXIO_IRQSTATUS(m) (0x308U + (m) * 0x80U) +#define CAL_CSI2_SHORT_PACKET(m) (0x30cU + (m) * 0x80U) +#define CAL_CSI2_COMPLEXIO_IRQENABLE(m) (0x310U + (m) * 0x80U) +#define CAL_CSI2_TIMING(m) (0x314U + (m) * 0x80U) +#define CAL_CSI2_VC_IRQENABLE(m) (0x318U + (m) * 0x80U) +#define CAL_CSI2_VC_IRQSTATUS(m) (0x328U + (m) * 0x80U) +#define CAL_CSI2_CTX0(m) (0x330U + (m) * 0x80U) +#define CAL_CSI2_CTX1(m) (0x334U + (m) * 0x80U) +#define CAL_CSI2_CTX2(m) (0x338U + (m) * 0x80U) +#define CAL_CSI2_CTX3(m) (0x33cU + (m) * 0x80U) +#define CAL_CSI2_CTX4(m) (0x340U + (m) * 0x80U) +#define CAL_CSI2_CTX5(m) (0x344U + (m) * 0x80U) +#define CAL_CSI2_CTX6(m) (0x348U + (m) * 0x80U) +#define CAL_CSI2_CTX7(m) (0x34cU + (m) * 0x80U) +#define CAL_CSI2_STATUS0(m) (0x350U + (m) * 0x80U) +#define CAL_CSI2_STATUS1(m) (0x354U + (m) * 0x80U) +#define CAL_CSI2_STATUS2(m) (0x358U + (m) * 0x80U) +#define CAL_CSI2_STATUS3(m) (0x35cU + (m) * 0x80U) +#define CAL_CSI2_STATUS4(m) (0x360U + (m) * 0x80U) +#define CAL_CSI2_STATUS5(m) (0x364U + (m) * 0x80U) +#define CAL_CSI2_STATUS6(m) (0x368U + (m) * 0x80U) +#define CAL_CSI2_STATUS7(m) (0x36cU + (m) * 0x80U) /* CAL CSI2 PHY register offsets */ #define CAL_CSI2_PHY_REG0 0x000 @@ -141,12 +141,12 @@ #define CAL_HL_IRQ_EOI_LINE_NUMBER_READ0 0 #define CAL_HL_IRQ_EOI_LINE_NUMBER_EOI0 0 -#define CAL_HL_IRQ_MASK(m) BIT((m) - 1) +#define CAL_HL_IRQ_MASK(m) BIT(m) #define CAL_HL_IRQ_OCPO_ERR_MASK BIT(6) -#define CAL_HL_IRQ_CIO_MASK(i) BIT(16 + ((i) - 1) * 8) -#define CAL_HL_IRQ_VC_MASK(i) BIT(17 + ((i) - 1) * 8) +#define CAL_HL_IRQ_CIO_MASK(i) BIT(16 + (i) * 8) +#define CAL_HL_IRQ_VC_MASK(i) BIT(17 + (i) * 8) #define CAL_PIX_PROC_EN_MASK BIT(0) #define CAL_PIX_PROC_EXTRACT_MASK GENMASK(4, 1) -- cgit v1.2.3 From 255502a723b5fecf01e7f02d19ba49d507c673d5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:26 +0200 Subject: media: ti-vpe: cal: Index IRQ registersstarting at 0 The datasheet numbers the IRQ registers starting from 0. Update the numbering scheme to match the datasheet. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 38 ++++++++++++++++---------------- drivers/media/platform/ti-vpe/cal_regs.h | 8 +++---- 2 files changed, 23 insertions(+), 23 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 799c25f5aafd..b04d8cb86977 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -695,22 +695,22 @@ static void enable_irqs(struct cal_ctx *ctx) CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; /* Enable CIO error irqs */ - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(1), + reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_CIO_MASK(ctx->csi2_port)); reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), cio_err_mask); /* Always enable OCPO error */ - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(1), CAL_HL_IRQ_OCPO_ERR_MASK); + reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); /* Enable IRQ_WDMA_END 0/1 */ val = 0; set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(2), val); + reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(1), val); /* Enable IRQ_WDMA_START 0/1 */ val = 0; set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(3), val); + reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(2), val); /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); } @@ -720,7 +720,7 @@ static void disable_irqs(struct cal_ctx *ctx) u32 val; /* Disable CIO error irqs */ - reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(1), + reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(0), CAL_HL_IRQ_CIO_MASK(ctx->csi2_port)); reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), 0); @@ -728,11 +728,11 @@ static void disable_irqs(struct cal_ctx *ctx) /* Disable IRQ_WDMA_END 0/1 */ val = 0; set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(2), val); + reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(1), val); /* Disable IRQ_WDMA_START 0/1 */ val = 0; set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(3), val); + reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(2), val); /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(0), 0); } @@ -1206,13 +1206,13 @@ static irqreturn_t cal_irq(int irq_cal, void *data) struct cal_dev *dev = (struct cal_dev *)data; struct cal_ctx *ctx; struct cal_dmaqueue *dma_q; - u32 irqst1, irqst2, irqst3; + u32 irqst0, irqst1, irqst2; - irqst1 = reg_read(dev, CAL_HL_IRQSTATUS(1)); - if (irqst1) { + irqst0 = reg_read(dev, CAL_HL_IRQSTATUS(0)); + if (irqst0) { int i; - reg_write(dev, CAL_HL_IRQSTATUS(1), irqst1); + reg_write(dev, CAL_HL_IRQSTATUS(0), irqst0); if (irqst1 & CAL_HL_IRQ_OCPO_ERR_MASK) dev_err_ratelimited(&dev->pdev->dev, "OCPO ERROR\n"); @@ -1232,15 +1232,15 @@ static irqreturn_t cal_irq(int irq_cal, void *data) } /* Check which DMA just finished */ - irqst2 = reg_read(dev, CAL_HL_IRQSTATUS(2)); - if (irqst2) { + irqst1 = reg_read(dev, CAL_HL_IRQSTATUS(1)); + if (irqst1) { int i; /* Clear Interrupt status */ - reg_write(dev, CAL_HL_IRQSTATUS(2), irqst2); + reg_write(dev, CAL_HL_IRQSTATUS(1), irqst1); for (i = 0; i < 2; ++i) { - if (isportirqset(irqst2, i)) { + if (isportirqset(irqst1, i)) { ctx = dev->ctx[i]; spin_lock(&ctx->slock); @@ -1255,15 +1255,15 @@ static irqreturn_t cal_irq(int irq_cal, void *data) } /* Check which DMA just started */ - irqst3 = reg_read(dev, CAL_HL_IRQSTATUS(3)); - if (irqst3) { + irqst2 = reg_read(dev, CAL_HL_IRQSTATUS(2)); + if (irqst2) { int i; /* Clear Interrupt status */ - reg_write(dev, CAL_HL_IRQSTATUS(3), irqst3); + reg_write(dev, CAL_HL_IRQSTATUS(2), irqst2); for (i = 0; i < 2; ++i) { - if (isportirqset(irqst3, i)) { + if (isportirqset(irqst2, i)) { ctx = dev->ctx[i]; dma_q = &ctx->vidq; diff --git a/drivers/media/platform/ti-vpe/cal_regs.h b/drivers/media/platform/ti-vpe/cal_regs.h index 56c8ccd0e75f..b551e67d5d41 100644 --- a/drivers/media/platform/ti-vpe/cal_regs.h +++ b/drivers/media/platform/ti-vpe/cal_regs.h @@ -42,10 +42,10 @@ #define CAL_HL_HWINFO 0x0004 #define CAL_HL_SYSCONFIG 0x0010 #define CAL_HL_IRQ_EOI 0x001c -#define CAL_HL_IRQSTATUS_RAW(m) (0x20U + ((m-1) * 0x10U)) -#define CAL_HL_IRQSTATUS(m) (0x24U + ((m-1) * 0x10U)) -#define CAL_HL_IRQENABLE_SET(m) (0x28U + ((m-1) * 0x10U)) -#define CAL_HL_IRQENABLE_CLR(m) (0x2cU + ((m-1) * 0x10U)) +#define CAL_HL_IRQSTATUS_RAW(m) (0x20U + (m) * 0x10U) +#define CAL_HL_IRQSTATUS(m) (0x24U + (m) * 0x10U) +#define CAL_HL_IRQENABLE_SET(m) (0x28U + (m) * 0x10U) +#define CAL_HL_IRQENABLE_CLR(m) (0x2cU + (m) * 0x10U) #define CAL_PIX_PROC(m) (0xc0U + (m) * 0x4U) #define CAL_CTRL 0x100 #define CAL_CTRL1 0x104 -- cgit v1.2.3 From 47bed3fbe8a0ced333fda22e4a0a29342782c793 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:27 +0200 Subject: media: ti-vpe: cal: Merge all status variables in IRQ handler The cal_irq() function reads three IRQ status registers and stores their values in three different variables. As each value is processed right after reading the corresponding register, a single variable is enough. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index b04d8cb86977..2aa28af7cad3 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1206,19 +1206,19 @@ static irqreturn_t cal_irq(int irq_cal, void *data) struct cal_dev *dev = (struct cal_dev *)data; struct cal_ctx *ctx; struct cal_dmaqueue *dma_q; - u32 irqst0, irqst1, irqst2; + u32 status; - irqst0 = reg_read(dev, CAL_HL_IRQSTATUS(0)); - if (irqst0) { + status = reg_read(dev, CAL_HL_IRQSTATUS(0)); + if (status) { int i; - reg_write(dev, CAL_HL_IRQSTATUS(0), irqst0); + reg_write(dev, CAL_HL_IRQSTATUS(0), status); - if (irqst1 & CAL_HL_IRQ_OCPO_ERR_MASK) + if (status & CAL_HL_IRQ_OCPO_ERR_MASK) dev_err_ratelimited(&dev->pdev->dev, "OCPO ERROR\n"); for (i = 0; i < 2; ++i) { - if (irqst1 & CAL_HL_IRQ_CIO_MASK(i)) { + if (status & CAL_HL_IRQ_CIO_MASK(i)) { u32 cio_stat = reg_read(dev, CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); @@ -1232,15 +1232,15 @@ static irqreturn_t cal_irq(int irq_cal, void *data) } /* Check which DMA just finished */ - irqst1 = reg_read(dev, CAL_HL_IRQSTATUS(1)); - if (irqst1) { + status = reg_read(dev, CAL_HL_IRQSTATUS(1)); + if (status) { int i; /* Clear Interrupt status */ - reg_write(dev, CAL_HL_IRQSTATUS(1), irqst1); + reg_write(dev, CAL_HL_IRQSTATUS(1), status); for (i = 0; i < 2; ++i) { - if (isportirqset(irqst1, i)) { + if (isportirqset(status, i)) { ctx = dev->ctx[i]; spin_lock(&ctx->slock); @@ -1255,15 +1255,15 @@ static irqreturn_t cal_irq(int irq_cal, void *data) } /* Check which DMA just started */ - irqst2 = reg_read(dev, CAL_HL_IRQSTATUS(2)); - if (irqst2) { + status = reg_read(dev, CAL_HL_IRQSTATUS(2)); + if (status) { int i; /* Clear Interrupt status */ - reg_write(dev, CAL_HL_IRQSTATUS(2), irqst2); + reg_write(dev, CAL_HL_IRQSTATUS(2), status); for (i = 0; i < 2; ++i) { - if (isportirqset(irqst2, i)) { + if (isportirqset(status, i)) { ctx = dev->ctx[i]; dma_q = &ctx->vidq; -- cgit v1.2.3 From d2b084eb1eecc130bc4c1e7defd45467f5d2077a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:28 +0200 Subject: media: ti-vpe: cal: Inline CAL_VERSION macro in its only user The CAL_VERSION macro is used once only, in MODULE_VERSION(). This doesn't improve readability, inline it. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 2aa28af7cad3..9b5ad48538c1 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -39,12 +39,10 @@ #define MAX_WIDTH_BYTES (8192 * 8) #define MAX_HEIGHT_LINES 16383 -#define CAL_VERSION "0.1.0" - MODULE_DESCRIPTION("TI CAL driver"); MODULE_AUTHOR("Benoit Parrot, "); MODULE_LICENSE("GPL v2"); -MODULE_VERSION(CAL_VERSION); +MODULE_VERSION("0.1.0"); static unsigned video_nr = -1; module_param(video_nr, uint, 0644); -- cgit v1.2.3 From 50797fb30b95b04332b9f3a35c9f7ec2a09692a1 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:29 +0200 Subject: media: ti-vpe: cal: Turn reg_(read|write)_field() into inline functions Turn the reg_(read|write)_field() macros into inline functions for additional type safety. Use the FIELD_GET() and FIELD_PREP() macros internally instead of reinventing the wheel. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 9b5ad48538c1..278477cf0ae9 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -6,6 +6,7 @@ * Benoit Parrot, */ +#include #include #include #include @@ -76,13 +77,6 @@ static const struct v4l2_fract #define reg_read(dev, offset) ioread32(dev->base + offset) #define reg_write(dev, offset, val) iowrite32(val, dev->base + offset) -#define reg_read_field(dev, offset, mask) get_field(reg_read(dev, offset), \ - mask) -#define reg_write_field(dev, offset, field, mask) { \ - u32 val = reg_read(dev, offset); \ - set_field(&val, field, mask); \ - reg_write(dev, offset, val); } - /* ------------------------------------------------------------------ * Basic structures * ------------------------------------------------------------------ @@ -418,6 +412,21 @@ struct cal_ctx { bool dma_act; }; +static inline u32 reg_read_field(struct cal_dev *dev, u32 offset, u32 mask) +{ + return FIELD_GET(mask, reg_read(dev, offset)); +} + +static inline void reg_write_field(struct cal_dev *dev, u32 offset, u32 value, + u32 mask) +{ + u32 val = reg_read(dev, offset); + + val &= ~mask; + val |= FIELD_PREP(mask, value); + reg_write(dev, offset, val); +} + static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx, u32 pixelformat) { @@ -453,11 +462,6 @@ static inline struct cal_ctx *notifier_to_ctx(struct v4l2_async_notifier *n) return container_of(n, struct cal_ctx, notifier); } -static inline int get_field(u32 value, u32 mask) -{ - return (value & mask) >> __ffs(mask); -} - static inline void set_field(u32 *valp, u32 field, u32 mask) { u32 val = *valp; -- cgit v1.2.3 From 37166b2ff964714efabefe4e6809d4af2d10a3e9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:30 +0200 Subject: media: ti-vpe: cal: Make cal_formats array const The static cal_formats array contains const data, make it const. This requires turning a few cal_fmt pointers into const pointers. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 278477cf0ae9..3668c4d445fe 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -89,7 +89,7 @@ struct cal_fmt { u8 bpp; }; -static struct cal_fmt cal_formats[] = { +static const struct cal_fmt cal_formats[] = { { .fourcc = V4L2_PIX_FMT_YUYV, .code = MEDIA_BUS_FMT_YUYV8_2X8, @@ -392,7 +392,7 @@ struct cal_ctx { struct v4l2_mbus_framefmt m_fmt; /* Current subdev enumerated format */ - struct cal_fmt *active_fmt[ARRAY_SIZE(cal_formats)]; + const struct cal_fmt *active_fmt[ARRAY_SIZE(cal_formats)]; int num_active_fmt; struct v4l2_fract timeperframe; @@ -1932,7 +1932,6 @@ static int cal_async_bound(struct v4l2_async_notifier *notifier, /* Enumerate sub device formats and enable all matching local formats */ ctx->num_active_fmt = 0; for (j = 0, i = 0; ret != -EINVAL; ++j) { - struct cal_fmt *fmt; memset(&mbus_code, 0, sizeof(mbus_code)); mbus_code.index = j; @@ -1947,7 +1946,7 @@ static int cal_async_bound(struct v4l2_async_notifier *notifier, subdev->name, mbus_code.code, j); for (k = 0; k < ARRAY_SIZE(cal_formats); k++) { - fmt = &cal_formats[k]; + const struct cal_fmt *fmt = &cal_formats[k]; if (mbus_code.code == fmt->code) { ctx->active_fmt[i] = fmt; -- cgit v1.2.3 From 6ff1d7260693cad1e06c24f99dc2f4d0cebb2589 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:31 +0200 Subject: media: ti-vpe: cal: Remove needless variable initialization A few local variables are needlessly initialized. Fix them. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 3668c4d445fe..d0390b23426d 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -616,8 +616,8 @@ static struct cc_data *cc_create(struct cal_dev *dev, unsigned int core) */ static void cal_get_hwinfo(struct cal_dev *dev) { - u32 revision = 0; - u32 hwinfo = 0; + u32 revision; + u32 hwinfo; revision = reg_read(dev, CAL_HL_REVISION); cal_dbg(3, dev, "CAL_HL_REVISION = 0x%08x (expecting 0x40000200)\n", @@ -1302,7 +1302,7 @@ static int cal_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt = NULL; + const struct cal_fmt *fmt; if (f->index >= ctx->num_active_fmt) return -EINVAL; @@ -1655,7 +1655,7 @@ static void cal_buffer_queue(struct vb2_buffer *vb) struct cal_buffer *buf = container_of(vb, struct cal_buffer, vb.vb2_buf); struct cal_dmaqueue *vidq = &ctx->vidq; - unsigned long flags = 0; + unsigned long flags; /* recheck locking */ spin_lock_irqsave(&ctx->slock, flags); @@ -1668,7 +1668,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) struct cal_ctx *ctx = vb2_get_drv_priv(vq); struct cal_dmaqueue *dma_q = &ctx->vidq; struct cal_buffer *buf, *tmp; - unsigned long addr = 0; + unsigned long addr; unsigned long flags; int ret; @@ -2008,7 +2008,7 @@ static struct device_node * of_get_next_port(const struct device_node *parent, struct device_node *prev) { - struct device_node *port = NULL; + struct device_node *port; if (!parent) return NULL; @@ -2052,7 +2052,7 @@ static struct device_node * of_get_next_endpoint(const struct device_node *parent, struct device_node *prev) { - struct device_node *ep = NULL; + struct device_node *ep; if (!parent) return NULL; @@ -2266,8 +2266,8 @@ static int cal_probe(struct platform_device *pdev) struct cal_dev *dev; struct cal_ctx *ctx; struct device_node *parent = pdev->dev.of_node; - struct regmap *syscon_camerrx = NULL; - u32 syscon_camerrx_offset = 0; + struct regmap *syscon_camerrx; + u32 syscon_camerrx_offset; int ret; int irq; int i; -- cgit v1.2.3 From d64463598f50b882a92e8b06ce4c8c338f818a95 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:32 +0200 Subject: media: ti-vpe: cal: Remove needless casts Casting from a void pointer to a struct pointer doesn't require a cast. Remove them. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index d0390b23426d..ee6f700ad190 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1205,7 +1205,7 @@ static inline void cal_process_buffer_complete(struct cal_ctx *ctx) static irqreturn_t cal_irq(int irq_cal, void *data) { - struct cal_dev *dev = (struct cal_dev *)data; + struct cal_dev *dev = data; struct cal_ctx *ctx; struct cal_dmaqueue *dma_q; u32 status; @@ -2404,8 +2404,7 @@ runtime_disable: static int cal_remove(struct platform_device *pdev) { - struct cal_dev *dev = - (struct cal_dev *)platform_get_drvdata(pdev); + struct cal_dev *dev = platform_get_drvdata(pdev); struct cal_ctx *ctx; int i; -- cgit v1.2.3 From fcc6d6f71d1f2363c557d8638174b14239c58021 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:33 +0200 Subject: media: ti-vpe: cal: Turn boolean variable into bool The found_port variable contains a boolean value, make it a bool. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index ee6f700ad190..edb1034f3b04 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2074,7 +2074,8 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst) struct v4l2_fwnode_endpoint *endpoint; struct v4l2_async_subdev *asd; u32 regval = 0; - int ret, index, found_port = 0, lane; + int ret, index, lane; + bool found_port = false; parent = pdev->dev.of_node; @@ -2099,7 +2100,7 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst) ctx_dbg(3, ctx, "port:%d inst:%d :%d\n", index, inst, regval); if ((regval == inst) && (index == inst)) { - found_port = 1; + found_port = true; break; } } -- cgit v1.2.3 From 6341aa2ee4dd955bd6cd11ddebd8cb5ee6e99a44 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:34 +0200 Subject: media: ti-vpe: cal: Make loop indices unsigned where applicable Many loop indices only take positive values. Make them unsigned. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index edb1034f3b04..579f823451f4 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -488,7 +488,7 @@ static int cal_camerarx_regmap_init(struct cal_dev *dev) { struct reg_field *field; struct cal_csi2_phy *phy; - int i, j; + unsigned int i, j; if (!dev->data) return -EINVAL; @@ -901,7 +901,7 @@ static void csi2_wait_for_phy(struct cal_ctx *ctx) static void csi2_phy_deinit(struct cal_ctx *ctx) { - int i; + unsigned int i; csi2_cio_power(ctx, false); @@ -1212,7 +1212,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) status = reg_read(dev, CAL_HL_IRQSTATUS(0)); if (status) { - int i; + unsigned int i; reg_write(dev, CAL_HL_IRQSTATUS(0), status); @@ -1225,7 +1225,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); dev_err_ratelimited(&dev->pdev->dev, - "CIO%d error: %#08x\n", i, cio_stat); + "CIO%u error: %#08x\n", i, cio_stat); reg_write(dev, CAL_CSI2_COMPLEXIO_IRQSTATUS(i), cio_stat); @@ -1236,7 +1236,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) /* Check which DMA just finished */ status = reg_read(dev, CAL_HL_IRQSTATUS(1)); if (status) { - int i; + unsigned int i; /* Clear Interrupt status */ reg_write(dev, CAL_HL_IRQSTATUS(1), status); @@ -1259,7 +1259,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) /* Check which DMA just started */ status = reg_read(dev, CAL_HL_IRQSTATUS(2)); if (status) { - int i; + unsigned int i; /* Clear Interrupt status */ reg_write(dev, CAL_HL_IRQSTATUS(2), status); @@ -1917,8 +1917,8 @@ static int cal_async_bound(struct v4l2_async_notifier *notifier, { struct cal_ctx *ctx = notifier_to_ctx(notifier); struct v4l2_subdev_mbus_code_enum mbus_code; + unsigned int i, j, k; int ret = 0; - int i, j, k; if (ctx->sensor) { ctx_info(ctx, "Rejecting subdev %s (Already set!!)", @@ -1942,7 +1942,7 @@ static int cal_async_bound(struct v4l2_async_notifier *notifier, continue; ctx_dbg(2, ctx, - "subdev %s: code: %04x idx: %d\n", + "subdev %s: code: %04x idx: %u\n", subdev->name, mbus_code.code, j); for (k = 0; k < ARRAY_SIZE(cal_formats); k++) { @@ -1951,7 +1951,7 @@ static int cal_async_bound(struct v4l2_async_notifier *notifier, if (mbus_code.code == fmt->code) { ctx->active_fmt[i] = fmt; ctx_dbg(2, ctx, - "matched fourcc: %s: code: %04x idx: %d\n", + "matched fourcc: %s: code: %04x idx: %u\n", fourcc_to_str(fmt->fourcc), fmt->code, i); ctx->num_active_fmt = ++i; @@ -2269,9 +2269,9 @@ static int cal_probe(struct platform_device *pdev) struct device_node *parent = pdev->dev.of_node; struct regmap *syscon_camerrx; u32 syscon_camerrx_offset; + unsigned int i; int ret; int irq; - int i; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) @@ -2407,7 +2407,7 @@ static int cal_remove(struct platform_device *pdev) { struct cal_dev *dev = platform_get_drvdata(pdev); struct cal_ctx *ctx; - int i; + unsigned int i; cal_dbg(1, dev, "Removing %s\n", CAL_MODULE_NAME); -- cgit v1.2.3 From 5b4426e33b85432e7cae803e622c2d383e313f66 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:35 +0200 Subject: media: ti-vpe: cal: Embed base_fields array in struct cal_csi2_phy The base_fields field of cal_csi2_phy points to a fixed-size array of struct reg_field. This level of indirection isn't needed, embed the array directly in struct cal_csi2_phy. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 70 ++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 40 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 579f823451f4..1f15481ba5a2 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -234,7 +234,7 @@ enum cal_camerarx_field { struct cal_csi2_phy { struct regmap_field *fields[F_MAX_FIELDS]; - struct reg_field *base_fields; + struct reg_field base_fields[F_MAX_FIELDS]; const int num_lanes; }; @@ -245,27 +245,23 @@ struct cal_data { const unsigned int flags; }; -static struct reg_field dra72x_ctrl_core_csi0_reg_fields[F_MAX_FIELDS] = { - [F_CTRLCLKEN] = REG_FIELD(0, 10, 10), - [F_CAMMODE] = REG_FIELD(0, 11, 12), - [F_LANEENABLE] = REG_FIELD(0, 13, 16), - [F_CSI_MODE] = REG_FIELD(0, 17, 17), -}; - -static struct reg_field dra72x_ctrl_core_csi1_reg_fields[F_MAX_FIELDS] = { - [F_CTRLCLKEN] = REG_FIELD(0, 0, 0), - [F_CAMMODE] = REG_FIELD(0, 1, 2), - [F_LANEENABLE] = REG_FIELD(0, 3, 4), - [F_CSI_MODE] = REG_FIELD(0, 5, 5), -}; - static struct cal_csi2_phy dra72x_cal_csi_phy[] = { { - .base_fields = dra72x_ctrl_core_csi0_reg_fields, + .base_fields = { + [F_CTRLCLKEN] = REG_FIELD(0, 10, 10), + [F_CAMMODE] = REG_FIELD(0, 11, 12), + [F_LANEENABLE] = REG_FIELD(0, 13, 16), + [F_CSI_MODE] = REG_FIELD(0, 17, 17), + }, .num_lanes = 4, }, { - .base_fields = dra72x_ctrl_core_csi1_reg_fields, + .base_fields = { + [F_CTRLCLKEN] = REG_FIELD(0, 0, 0), + [F_CAMMODE] = REG_FIELD(0, 1, 2), + [F_LANEENABLE] = REG_FIELD(0, 3, 4), + [F_CSI_MODE] = REG_FIELD(0, 5, 5), + }, .num_lanes = 2, }, }; @@ -281,27 +277,23 @@ static const struct cal_data dra72x_es1_cal_data = { .flags = DRA72_CAL_PRE_ES2_LDO_DISABLE, }; -static struct reg_field dra76x_ctrl_core_csi0_reg_fields[F_MAX_FIELDS] = { - [F_CTRLCLKEN] = REG_FIELD(0, 8, 8), - [F_CAMMODE] = REG_FIELD(0, 9, 10), - [F_CSI_MODE] = REG_FIELD(0, 11, 11), - [F_LANEENABLE] = REG_FIELD(0, 27, 31), -}; - -static struct reg_field dra76x_ctrl_core_csi1_reg_fields[F_MAX_FIELDS] = { - [F_CTRLCLKEN] = REG_FIELD(0, 0, 0), - [F_CAMMODE] = REG_FIELD(0, 1, 2), - [F_CSI_MODE] = REG_FIELD(0, 3, 3), - [F_LANEENABLE] = REG_FIELD(0, 24, 26), -}; - static struct cal_csi2_phy dra76x_cal_csi_phy[] = { { - .base_fields = dra76x_ctrl_core_csi0_reg_fields, + .base_fields = { + [F_CTRLCLKEN] = REG_FIELD(0, 8, 8), + [F_CAMMODE] = REG_FIELD(0, 9, 10), + [F_CSI_MODE] = REG_FIELD(0, 11, 11), + [F_LANEENABLE] = REG_FIELD(0, 27, 31), + }, .num_lanes = 5, }, { - .base_fields = dra76x_ctrl_core_csi1_reg_fields, + .base_fields = { + [F_CTRLCLKEN] = REG_FIELD(0, 0, 0), + [F_CAMMODE] = REG_FIELD(0, 1, 2), + [F_CSI_MODE] = REG_FIELD(0, 3, 3), + [F_LANEENABLE] = REG_FIELD(0, 24, 26), + }, .num_lanes = 3, }, }; @@ -311,15 +303,13 @@ static const struct cal_data dra76x_cal_data = { .num_csi2_phy = ARRAY_SIZE(dra76x_cal_csi_phy), }; -static struct reg_field am654_ctrl_core_csi0_reg_fields[F_MAX_FIELDS] = { - [F_CTRLCLKEN] = REG_FIELD(0, 15, 15), - [F_CAMMODE] = REG_FIELD(0, 24, 25), - [F_LANEENABLE] = REG_FIELD(0, 0, 4), -}; - static struct cal_csi2_phy am654_cal_csi_phy[] = { { - .base_fields = am654_ctrl_core_csi0_reg_fields, + .base_fields = { + [F_CTRLCLKEN] = REG_FIELD(0, 15, 15), + [F_CAMMODE] = REG_FIELD(0, 24, 25), + [F_LANEENABLE] = REG_FIELD(0, 0, 4), + }, .num_lanes = 5, }, }; -- cgit v1.2.3 From 649e9535f15772be3b4cfbd621a9327db7b0289a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:36 +0200 Subject: media: ti-vpe: cal: Don't modify cal_csi2_phy base_fields In preparation to constify cal_csi2_phy, avoid modifying the base_fields array at runtime. As the array now only needs to stored two values instead of a full struct reg_field instance, save memory by using a custom structure for the base_fields elements. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 54 ++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 24 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 1f15481ba5a2..ff2afcfdff01 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -234,7 +234,10 @@ enum cal_camerarx_field { struct cal_csi2_phy { struct regmap_field *fields[F_MAX_FIELDS]; - struct reg_field base_fields[F_MAX_FIELDS]; + struct { + unsigned int lsb; + unsigned int msb; + } base_fields[F_MAX_FIELDS]; const int num_lanes; }; @@ -248,19 +251,19 @@ struct cal_data { static struct cal_csi2_phy dra72x_cal_csi_phy[] = { { .base_fields = { - [F_CTRLCLKEN] = REG_FIELD(0, 10, 10), - [F_CAMMODE] = REG_FIELD(0, 11, 12), - [F_LANEENABLE] = REG_FIELD(0, 13, 16), - [F_CSI_MODE] = REG_FIELD(0, 17, 17), + [F_CTRLCLKEN] = { 10, 10 }, + [F_CAMMODE] = { 11, 12 }, + [F_LANEENABLE] = { 13, 16 }, + [F_CSI_MODE] = { 17, 17 }, }, .num_lanes = 4, }, { .base_fields = { - [F_CTRLCLKEN] = REG_FIELD(0, 0, 0), - [F_CAMMODE] = REG_FIELD(0, 1, 2), - [F_LANEENABLE] = REG_FIELD(0, 3, 4), - [F_CSI_MODE] = REG_FIELD(0, 5, 5), + [F_CTRLCLKEN] = { 0, 0 }, + [F_CAMMODE] = { 1, 2 }, + [F_LANEENABLE] = { 3, 4 }, + [F_CSI_MODE] = { 5, 5 }, }, .num_lanes = 2, }, @@ -280,19 +283,19 @@ static const struct cal_data dra72x_es1_cal_data = { static struct cal_csi2_phy dra76x_cal_csi_phy[] = { { .base_fields = { - [F_CTRLCLKEN] = REG_FIELD(0, 8, 8), - [F_CAMMODE] = REG_FIELD(0, 9, 10), - [F_CSI_MODE] = REG_FIELD(0, 11, 11), - [F_LANEENABLE] = REG_FIELD(0, 27, 31), + [F_CTRLCLKEN] = { 8, 8 }, + [F_CAMMODE] = { 9, 10 }, + [F_CSI_MODE] = { 11, 11 }, + [F_LANEENABLE] = { 27, 31 }, }, .num_lanes = 5, }, { .base_fields = { - [F_CTRLCLKEN] = REG_FIELD(0, 0, 0), - [F_CAMMODE] = REG_FIELD(0, 1, 2), - [F_CSI_MODE] = REG_FIELD(0, 3, 3), - [F_LANEENABLE] = REG_FIELD(0, 24, 26), + [F_CTRLCLKEN] = { 0, 0 }, + [F_CAMMODE] = { 1, 2 }, + [F_CSI_MODE] = { 3, 3 }, + [F_LANEENABLE] = { 24, 26 }, }, .num_lanes = 3, }, @@ -306,9 +309,9 @@ static const struct cal_data dra76x_cal_data = { static struct cal_csi2_phy am654_cal_csi_phy[] = { { .base_fields = { - [F_CTRLCLKEN] = REG_FIELD(0, 15, 15), - [F_CAMMODE] = REG_FIELD(0, 24, 25), - [F_LANEENABLE] = REG_FIELD(0, 0, 4), + [F_CTRLCLKEN] = { 15, 15 }, + [F_CAMMODE] = { 24, 25 }, + [F_LANEENABLE] = { 0, 4 }, }, .num_lanes = 5, }, @@ -476,7 +479,6 @@ static u32 cal_data_get_num_csi2_phy(struct cal_dev *dev) static int cal_camerarx_regmap_init(struct cal_dev *dev) { - struct reg_field *field; struct cal_csi2_phy *phy; unsigned int i, j; @@ -486,16 +488,20 @@ static int cal_camerarx_regmap_init(struct cal_dev *dev) for (i = 0; i < cal_data_get_num_csi2_phy(dev); i++) { phy = &dev->data->csi2_phy_core[i]; for (j = 0; j < F_MAX_FIELDS; j++) { - field = &phy->base_fields[j]; + struct reg_field field = { + .reg = dev->syscon_camerrx_offset, + .lsb = phy->base_fields[j].lsb, + .msb = phy->base_fields[j].msb, + }; + /* * Here we update the reg offset with the * value found in DT */ - field->reg = dev->syscon_camerrx_offset; phy->fields[j] = devm_regmap_field_alloc(&dev->pdev->dev, dev->syscon_camerrx, - *field); + field); if (IS_ERR(phy->fields[j])) { cal_err(dev, "Unable to allocate regmap fields\n"); return PTR_ERR(phy->fields[j]); -- cgit v1.2.3 From 5e19ffe77c096eab64f8a7893d58d80e725844c1 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:37 +0200 Subject: media: ti-vpe: cal: Store PHY regmap fields in struct cc_data The PHY regmap fields are created dynamically at runtime. They don't belong to the platform data that should be constant. Move them to the cc_data structure. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 92 +++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 45 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index ff2afcfdff01..3df97708e044 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -215,13 +215,6 @@ struct cal_dmaqueue { int ini_jiffies; }; -struct cc_data { - void __iomem *base; - struct resource *res; - - struct platform_device *pdev; -}; - /* CTRL_CORE_CAMERRX_CONTROL register field id */ enum cal_camerarx_field { F_CTRLCLKEN, @@ -232,8 +225,17 @@ enum cal_camerarx_field { F_MAX_FIELDS, }; +struct cc_data { + void __iomem *base; + struct resource *res; + struct platform_device *pdev; + + struct { + struct regmap_field *fields[F_MAX_FIELDS]; + } phy; +}; + struct cal_csi2_phy { - struct regmap_field *fields[F_MAX_FIELDS]; struct { unsigned int lsb; unsigned int msb; @@ -477,37 +479,37 @@ static u32 cal_data_get_num_csi2_phy(struct cal_dev *dev) return dev->data->num_csi2_phy; } -static int cal_camerarx_regmap_init(struct cal_dev *dev) +static int cal_camerarx_regmap_init(struct cal_dev *dev, struct cc_data *cc, + unsigned int idx) { - struct cal_csi2_phy *phy; - unsigned int i, j; + const struct cal_csi2_phy *phy; + unsigned int i; if (!dev->data) return -EINVAL; - for (i = 0; i < cal_data_get_num_csi2_phy(dev); i++) { - phy = &dev->data->csi2_phy_core[i]; - for (j = 0; j < F_MAX_FIELDS; j++) { - struct reg_field field = { - .reg = dev->syscon_camerrx_offset, - .lsb = phy->base_fields[j].lsb, - .msb = phy->base_fields[j].msb, - }; - - /* - * Here we update the reg offset with the - * value found in DT - */ - phy->fields[j] = - devm_regmap_field_alloc(&dev->pdev->dev, - dev->syscon_camerrx, - field); - if (IS_ERR(phy->fields[j])) { - cal_err(dev, "Unable to allocate regmap fields\n"); - return PTR_ERR(phy->fields[j]); - } + phy = &dev->data->csi2_phy_core[idx]; + + for (i = 0; i < F_MAX_FIELDS; i++) { + struct reg_field field = { + .reg = dev->syscon_camerrx_offset, + .lsb = phy->base_fields[i].lsb, + .msb = phy->base_fields[i].msb, + }; + + /* + * Here we update the reg offset with the + * value found in DT + */ + cc->phy.fields[i] = devm_regmap_field_alloc(&dev->pdev->dev, + dev->syscon_camerrx, + field); + if (IS_ERR(cc->phy.fields[i])) { + cal_err(dev, "Unable to allocate regmap fields\n"); + return PTR_ERR(cc->phy.fields[i]); } } + return 0; } @@ -554,28 +556,26 @@ static struct regmap *cal_get_camerarx_regmap(struct cal_dev *dev) */ static void camerarx_phy_enable(struct cal_ctx *ctx) { - struct cal_csi2_phy *phy; u32 phy_id = ctx->csi2_port; + struct cc_data *cc = ctx->dev->cc[phy_id]; u32 max_lanes; - phy = &ctx->dev->data->csi2_phy_core[phy_id]; - regmap_field_write(phy->fields[F_CAMMODE], 0); + regmap_field_write(cc->phy.fields[F_CAMMODE], 0); /* Always enable all lanes at the phy control level */ max_lanes = (1 << cal_data_get_phy_max_lanes(ctx)) - 1; - regmap_field_write(phy->fields[F_LANEENABLE], max_lanes); + regmap_field_write(cc->phy.fields[F_LANEENABLE], max_lanes); /* F_CSI_MODE is not present on every architecture */ - if (phy->fields[F_CSI_MODE]) - regmap_field_write(phy->fields[F_CSI_MODE], 1); - regmap_field_write(phy->fields[F_CTRLCLKEN], 1); + if (cc->phy.fields[F_CSI_MODE]) + regmap_field_write(cc->phy.fields[F_CSI_MODE], 1); + regmap_field_write(cc->phy.fields[F_CTRLCLKEN], 1); } static void camerarx_phy_disable(struct cal_ctx *ctx) { - struct cal_csi2_phy *phy; u32 phy_id = ctx->csi2_port; + struct cc_data *cc = ctx->dev->cc[phy_id]; - phy = &ctx->dev->data->csi2_phy_core[phy_id]; - regmap_field_write(phy->fields[F_CTRLCLKEN], 0); + regmap_field_write(cc->phy.fields[F_CTRLCLKEN], 0); } /* @@ -585,6 +585,7 @@ static struct cc_data *cc_create(struct cal_dev *dev, unsigned int core) { struct platform_device *pdev = dev->pdev; struct cc_data *cc; + int ret; cc = devm_kzalloc(&pdev->dev, sizeof(*cc), GFP_KERNEL); if (!cc) @@ -604,6 +605,10 @@ static struct cc_data *cc_create(struct cal_dev *dev, unsigned int core) cal_dbg(1, dev, "ioresource %s at %pa - %pa\n", cc->res->name, &cc->res->start, &cc->res->end); + ret = cal_camerarx_regmap_init(dev, cc, core); + if (ret) + return ERR_PTR(ret); + return cc; } @@ -2322,9 +2327,6 @@ static int cal_probe(struct platform_device *pdev) dev->syscon_camerrx = syscon_camerrx; dev->syscon_camerrx_offset = syscon_camerrx_offset; - ret = cal_camerarx_regmap_init(dev); - if (ret) - return ret; dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cal_top"); -- cgit v1.2.3 From be19b49e6ed6963ece660d33b4a16ea5b956faae Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:38 +0200 Subject: media: ti-vpe: cal: Rename cal_csi2_phy base_fields to fields The cal_csi2_phy structure has lost its 'fields' field that used to clash with the 'base_fields' field. Rename base_fields to fields. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 3df97708e044..72d5a6434d9e 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -239,7 +239,7 @@ struct cal_csi2_phy { struct { unsigned int lsb; unsigned int msb; - } base_fields[F_MAX_FIELDS]; + } fields[F_MAX_FIELDS]; const int num_lanes; }; @@ -252,7 +252,7 @@ struct cal_data { static struct cal_csi2_phy dra72x_cal_csi_phy[] = { { - .base_fields = { + .fields = { [F_CTRLCLKEN] = { 10, 10 }, [F_CAMMODE] = { 11, 12 }, [F_LANEENABLE] = { 13, 16 }, @@ -261,7 +261,7 @@ static struct cal_csi2_phy dra72x_cal_csi_phy[] = { .num_lanes = 4, }, { - .base_fields = { + .fields = { [F_CTRLCLKEN] = { 0, 0 }, [F_CAMMODE] = { 1, 2 }, [F_LANEENABLE] = { 3, 4 }, @@ -284,7 +284,7 @@ static const struct cal_data dra72x_es1_cal_data = { static struct cal_csi2_phy dra76x_cal_csi_phy[] = { { - .base_fields = { + .fields = { [F_CTRLCLKEN] = { 8, 8 }, [F_CAMMODE] = { 9, 10 }, [F_CSI_MODE] = { 11, 11 }, @@ -293,7 +293,7 @@ static struct cal_csi2_phy dra76x_cal_csi_phy[] = { .num_lanes = 5, }, { - .base_fields = { + .fields = { [F_CTRLCLKEN] = { 0, 0 }, [F_CAMMODE] = { 1, 2 }, [F_CSI_MODE] = { 3, 3 }, @@ -310,7 +310,7 @@ static const struct cal_data dra76x_cal_data = { static struct cal_csi2_phy am654_cal_csi_phy[] = { { - .base_fields = { + .fields = { [F_CTRLCLKEN] = { 15, 15 }, [F_CAMMODE] = { 24, 25 }, [F_LANEENABLE] = { 0, 4 }, @@ -493,8 +493,8 @@ static int cal_camerarx_regmap_init(struct cal_dev *dev, struct cc_data *cc, for (i = 0; i < F_MAX_FIELDS; i++) { struct reg_field field = { .reg = dev->syscon_camerrx_offset, - .lsb = phy->base_fields[i].lsb, - .msb = phy->base_fields[i].msb, + .lsb = phy->fields[i].lsb, + .msb = phy->fields[i].msb, }; /* -- cgit v1.2.3 From 01bd7499393217ee409f095f361dddda370f94a3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:39 +0200 Subject: media: ti-vpe: cal: Make structure fields unsigned where applicable Several structure fields declared as int store positive values only. Make them unsigned. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 72d5a6434d9e..e7e1ad428074 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -240,11 +240,11 @@ struct cal_csi2_phy { unsigned int lsb; unsigned int msb; } fields[F_MAX_FIELDS]; - const int num_lanes; + const unsigned int num_lanes; }; struct cal_data { - const int num_csi2_phy; + const unsigned int num_csi2_phy; struct cal_csi2_phy *csi2_phy_core; const unsigned int flags; @@ -388,7 +388,7 @@ struct cal_ctx { /* Current subdev enumerated format */ const struct cal_fmt *active_fmt[ARRAY_SIZE(cal_formats)]; - int num_active_fmt; + unsigned int num_active_fmt; struct v4l2_fract timeperframe; unsigned int sequence; -- cgit v1.2.3 From 9704762aed23aa0eb9fc88454c7ce423471bcb3a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:40 +0200 Subject: media: ti-vpe: cal: Constify platform data Now that no platform data fields are modified at runtime, make the corresponding structures const. The const qualifier for integer fields is removed as it's not required anymore. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index e7e1ad428074..86e67b98dc63 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -240,17 +240,17 @@ struct cal_csi2_phy { unsigned int lsb; unsigned int msb; } fields[F_MAX_FIELDS]; - const unsigned int num_lanes; + unsigned int num_lanes; }; struct cal_data { - const unsigned int num_csi2_phy; - struct cal_csi2_phy *csi2_phy_core; + unsigned int num_csi2_phy; + const struct cal_csi2_phy *csi2_phy_core; - const unsigned int flags; + unsigned int flags; }; -static struct cal_csi2_phy dra72x_cal_csi_phy[] = { +static const struct cal_csi2_phy dra72x_cal_csi_phy[] = { { .fields = { [F_CTRLCLKEN] = { 10, 10 }, @@ -282,7 +282,7 @@ static const struct cal_data dra72x_es1_cal_data = { .flags = DRA72_CAL_PRE_ES2_LDO_DISABLE, }; -static struct cal_csi2_phy dra76x_cal_csi_phy[] = { +static const struct cal_csi2_phy dra76x_cal_csi_phy[] = { { .fields = { [F_CTRLCLKEN] = { 8, 8 }, @@ -308,7 +308,7 @@ static const struct cal_data dra76x_cal_data = { .num_csi2_phy = ARRAY_SIZE(dra76x_cal_csi_phy), }; -static struct cal_csi2_phy am654_cal_csi_phy[] = { +static const struct cal_csi2_phy am654_cal_csi_phy[] = { { .fields = { [F_CTRLCLKEN] = { 15, 15 }, -- cgit v1.2.3 From ed641dc98dd624dfa12824611e339c6499e5f9fe Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:41 +0200 Subject: media: ti-vpe: cal: Remove static const cal_regmap_config template The global static const cal_regmap_config template is only used in a single location, to initialize 3 fields of a local variable. Two of those fields then get overwritten. Remove the template and set the last remaining field manually. Simplify the code by hardcoding the field values instead of calculating them for a variable that always has the same value. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 86e67b98dc63..15f4cd205e10 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -513,19 +513,12 @@ static int cal_camerarx_regmap_init(struct cal_dev *dev, struct cc_data *cc, return 0; } -static const struct regmap_config cal_regmap_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, -}; - static struct regmap *cal_get_camerarx_regmap(struct cal_dev *dev) { struct platform_device *pdev = dev->pdev; + struct regmap_config config = { }; struct regmap *regmap; void __iomem *base; - u32 reg_io_width; - struct regmap_config r_config = cal_regmap_config; struct resource *res; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, @@ -539,12 +532,12 @@ static struct regmap *cal_get_camerarx_regmap(struct cal_dev *dev) cal_dbg(1, dev, "ioresource %s at %pa - %pa\n", res->name, &res->start, &res->end); - reg_io_width = 4; - r_config.reg_stride = reg_io_width; - r_config.val_bits = reg_io_width * 8; - r_config.max_register = resource_size(res) - reg_io_width; + config.reg_bits = 32; + config.reg_stride = 4; + config.val_bits = 32; + config.max_register = resource_size(res) - 4; - regmap = regmap_init_mmio(NULL, base, &r_config); + regmap = regmap_init_mmio(NULL, base, &config); if (IS_ERR(regmap)) pr_err("regmap init failed\n"); -- cgit v1.2.3 From 6aa4459bdedc171c0d7d66a16f76131d333111f6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:42 +0200 Subject: media: ti-vpe: cal: Remove unused structure fields Remove structure fields that are never set, set and never read, or set to a fixed value. This allows removal of a global variable and a macro. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 34 +++------------------------------- 1 file changed, 3 insertions(+), 31 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 15f4cd205e10..89f6fd2e1300 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -53,10 +53,6 @@ static unsigned debug; module_param(debug, uint, 0644); MODULE_PARM_DESC(debug, "activates debug info"); -/* timeperframe: min/max and default */ -static const struct v4l2_fract - tpf_default = {.numerator = 1001, .denominator = 30000}; - #define cal_dbg(level, caldev, fmt, arg...) \ v4l2_dbg(level, debug, &caldev->v4l2_dev, fmt, ##arg) #define cal_info(caldev, fmt, arg...) \ @@ -71,7 +67,6 @@ static const struct v4l2_fract #define ctx_err(ctx, fmt, arg...) \ v4l2_err(&ctx->v4l2_dev, fmt, ##arg) -#define CAL_NUM_INPUT 1 #define CAL_NUM_CONTEXT 2 #define reg_read(dev, offset) ioread32(dev->base + offset) @@ -204,15 +199,10 @@ struct cal_buffer { /* common v4l buffer stuff -- must be first */ struct vb2_v4l2_buffer vb; struct list_head list; - const struct cal_fmt *fmt; }; struct cal_dmaqueue { struct list_head active; - - /* Counters to control fps rate */ - int frame; - int ini_jiffies; }; /* CTRL_CORE_CAMERRX_CONTROL register field id */ @@ -362,7 +352,6 @@ struct cal_ctx { struct v4l2_subdev *sensor; struct v4l2_fwnode_endpoint endpoint; - struct v4l2_fh fh; struct cal_dev *dev; struct cc_data *cc; @@ -371,14 +360,8 @@ struct cal_ctx { /* v4l2 buffers lock */ spinlock_t slock; - /* Several counters */ - unsigned long jiffies; - struct cal_dmaqueue vidq; - /* Input Number */ - int input; - /* video capture */ const struct cal_fmt *fmt; /* Used to store current pixel format */ @@ -390,11 +373,9 @@ struct cal_ctx { const struct cal_fmt *active_fmt[ARRAY_SIZE(cal_formats)]; unsigned int num_active_fmt; - struct v4l2_fract timeperframe; unsigned int sequence; unsigned int external_rate; struct vb2_queue vb_vidq; - unsigned int seq_count; unsigned int csi2_port; unsigned int cport; unsigned int virtual_channel; @@ -1538,7 +1519,7 @@ static int cal_enum_framesizes(struct file *file, void *fh, static int cal_enum_input(struct file *file, void *priv, struct v4l2_input *inp) { - if (inp->index >= CAL_NUM_INPUT) + if (inp->index > 0) return -EINVAL; inp->type = V4L2_INPUT_TYPE_CAMERA; @@ -1548,21 +1529,13 @@ static int cal_enum_input(struct file *file, void *priv, static int cal_g_input(struct file *file, void *priv, unsigned int *i) { - struct cal_ctx *ctx = video_drvdata(file); - - *i = ctx->input; + *i = 0; return 0; } static int cal_s_input(struct file *file, void *priv, unsigned int i) { - struct cal_ctx *ctx = video_drvdata(file); - - if (i >= CAL_NUM_INPUT) - return -EINVAL; - - ctx->input = i; - return 0; + return i > 0 ? -EINVAL : 0; } /* timeperframe is arbitrary and continuous */ @@ -1856,7 +1829,6 @@ static int cal_complete_ctx(struct cal_ctx *ctx) struct vb2_queue *q; int ret; - ctx->timeperframe = tpf_default; ctx->external_rate = 192000000; /* initialize locks */ -- cgit v1.2.3 From 0a19960a6c6cdeee9327c71d4f0d9dee199fb887 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:43 +0200 Subject: media: ti-vpe: cal: Remove flags field from struct cal_dev The cal_dev flags field is a copy of data->flags. Use the latter and remove the former. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 89f6fd2e1300..d04caa4fa9cf 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -326,9 +326,6 @@ struct cal_dev { struct platform_device *pdev; struct v4l2_device v4l2_dev; - /* Controller flags for special cases */ - unsigned int flags; - const struct cal_data *data; /* Control Module handle */ @@ -2249,8 +2246,6 @@ static int cal_probe(struct platform_device *pdev) return -ENODEV; } - dev->flags = dev->data->flags; - /* set pseudo v4l2 device name so we can use v4l2_printk */ strscpy(dev->v4l2_dev.name, CAL_MODULE_NAME, sizeof(dev->v4l2_dev.name)); @@ -2402,7 +2397,7 @@ static int cal_runtime_resume(struct device *dev) { struct cal_dev *caldev = dev_get_drvdata(dev); - if (caldev->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) { + if (caldev->data->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) { /* * Apply errata on both port everytime we (re-)enable * the clock -- cgit v1.2.3 From ca486468503652dbf55c4b1ffa9f3f3c4f3775bb Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:44 +0200 Subject: media: ti-vpe: cal: Move function to avoid forward declaration Move the csi2_phy_config() function to avoid its forward declaration. No functional change is included. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 98 ++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 50 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index d04caa4fa9cf..5db8d928cf09 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -740,7 +740,54 @@ static void csi2_cio_power(struct cal_ctx *ctx, bool enable) enable ? "up" : "down"); } -static void csi2_phy_config(struct cal_ctx *ctx); +/* + * TCLK values are OK at their reset values + */ +#define TCLK_TERM 0 +#define TCLK_MISS 1 +#define TCLK_SETTLE 14 + +static void csi2_phy_config(struct cal_ctx *ctx) +{ + unsigned int reg0, reg1; + unsigned int ths_term, ths_settle; + unsigned int csi2_ddrclk_khz; + struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = + &ctx->endpoint.bus.mipi_csi2; + u32 num_lanes = mipi_csi2->num_data_lanes; + + /* DPHY timing configuration */ + /* CSI-2 is DDR and we only count used lanes. */ + csi2_ddrclk_khz = ctx->external_rate / 1000 + / (2 * num_lanes) * ctx->fmt->bpp; + ctx_dbg(1, ctx, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); + + /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ + ths_term = 20 * csi2_ddrclk_khz / 1000000; + ctx_dbg(1, ctx, "ths_term: %d (0x%02x)\n", ths_term, ths_term); + + /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ + ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; + ctx_dbg(1, ctx, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); + + reg0 = reg_read(ctx->cc, CAL_CSI2_PHY_REG0); + set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, + CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); + set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); + set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); + + ctx_dbg(1, ctx, "CSI2_%d_REG0 = 0x%08x\n", ctx->csi2_port, reg0); + reg_write(ctx->cc, CAL_CSI2_PHY_REG0, reg0); + + reg1 = reg_read(ctx->cc, CAL_CSI2_PHY_REG1); + set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); + set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); + set_field(®1, TCLK_MISS, CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); + set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); + + ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x\n", ctx->csi2_port, reg1); + reg_write(ctx->cc, CAL_CSI2_PHY_REG1, reg1); +} static void csi2_phy_init(struct cal_ctx *ctx) { @@ -1077,55 +1124,6 @@ static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) reg_write(ctx->dev, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr); } -/* - * TCLK values are OK at their reset values - */ -#define TCLK_TERM 0 -#define TCLK_MISS 1 -#define TCLK_SETTLE 14 - -static void csi2_phy_config(struct cal_ctx *ctx) -{ - unsigned int reg0, reg1; - unsigned int ths_term, ths_settle; - unsigned int csi2_ddrclk_khz; - struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &ctx->endpoint.bus.mipi_csi2; - u32 num_lanes = mipi_csi2->num_data_lanes; - - /* DPHY timing configuration */ - /* CSI-2 is DDR and we only count used lanes. */ - csi2_ddrclk_khz = ctx->external_rate / 1000 - / (2 * num_lanes) * ctx->fmt->bpp; - ctx_dbg(1, ctx, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); - - /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ - ths_term = 20 * csi2_ddrclk_khz / 1000000; - ctx_dbg(1, ctx, "ths_term: %d (0x%02x)\n", ths_term, ths_term); - - /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ - ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; - ctx_dbg(1, ctx, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); - - reg0 = reg_read(ctx->cc, CAL_CSI2_PHY_REG0); - set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, - CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); - set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); - set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); - - ctx_dbg(1, ctx, "CSI2_%d_REG0 = 0x%08x\n", ctx->csi2_port, reg0); - reg_write(ctx->cc, CAL_CSI2_PHY_REG0, reg0); - - reg1 = reg_read(ctx->cc, CAL_CSI2_PHY_REG1); - set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); - set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); - set_field(®1, TCLK_MISS, CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); - set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); - - ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x\n", ctx->csi2_port, reg1); - reg_write(ctx->cc, CAL_CSI2_PHY_REG1, reg1); -} - static int cal_get_external_info(struct cal_ctx *ctx) { struct v4l2_ctrl *ctrl; -- cgit v1.2.3 From a7f8ac542a070cace1d1cc7f3e02f7791851a39f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:45 +0200 Subject: media: ti-vpe: cal: Rename cc_data to cal_camerarx The various data structures that describe the components of the camera access layer (CAL) are named without much consistency. Start cleaning this up by renaming the structure that describes the CAMERARX block, cc_data, to cal_camerarx. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 46 ++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 19 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 5db8d928cf09..621cc3f26762 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -215,16 +215,6 @@ enum cal_camerarx_field { F_MAX_FIELDS, }; -struct cc_data { - void __iomem *base; - struct resource *res; - struct platform_device *pdev; - - struct { - struct regmap_field *fields[F_MAX_FIELDS]; - } phy; -}; - struct cal_csi2_phy { struct { unsigned int lsb; @@ -315,9 +305,26 @@ static const struct cal_data am654_cal_data = { }; /* - * there is one cal_dev structure in the driver, it is shared by - * all instances. + * The Camera Adaptation Layer (CAL) module is paired with one or more complex + * I/O PHYs (CAMERARX). It contains multiple instances of CSI-2, processing and + * DMA contexts. + * + * The cal_dev structure represents the whole subsystem, including the CAL and + * the CAMERARX instances. The cal_camerarx structure represents one CAMERARX + * instance. The cal_ctx structure represents the combination of one CSI-2 + * context, one processing context and one DMA context. */ + +struct cal_camerarx { + void __iomem *base; + struct resource *res; + struct platform_device *pdev; + + struct { + struct regmap_field *fields[F_MAX_FIELDS]; + } phy; +}; + struct cal_dev { struct clk *fclk; int irq; @@ -333,7 +340,7 @@ struct cal_dev { u32 syscon_camerrx_offset; /* Camera Core Module handle */ - struct cc_data *cc[CAL_NUM_CSI2_PORTS]; + struct cal_camerarx *cc[CAL_NUM_CSI2_PORTS]; struct cal_ctx *ctx[CAL_NUM_CONTEXT]; }; @@ -350,7 +357,7 @@ struct cal_ctx { struct v4l2_fwnode_endpoint endpoint; struct cal_dev *dev; - struct cc_data *cc; + struct cal_camerarx *cc; /* v4l2_ioctl mutex */ struct mutex mutex; @@ -457,7 +464,8 @@ static u32 cal_data_get_num_csi2_phy(struct cal_dev *dev) return dev->data->num_csi2_phy; } -static int cal_camerarx_regmap_init(struct cal_dev *dev, struct cc_data *cc, +static int cal_camerarx_regmap_init(struct cal_dev *dev, + struct cal_camerarx *cc, unsigned int idx) { const struct cal_csi2_phy *phy; @@ -528,7 +536,7 @@ static struct regmap *cal_get_camerarx_regmap(struct cal_dev *dev) static void camerarx_phy_enable(struct cal_ctx *ctx) { u32 phy_id = ctx->csi2_port; - struct cc_data *cc = ctx->dev->cc[phy_id]; + struct cal_camerarx *cc = ctx->dev->cc[phy_id]; u32 max_lanes; regmap_field_write(cc->phy.fields[F_CAMMODE], 0); @@ -544,7 +552,7 @@ static void camerarx_phy_enable(struct cal_ctx *ctx) static void camerarx_phy_disable(struct cal_ctx *ctx) { u32 phy_id = ctx->csi2_port; - struct cc_data *cc = ctx->dev->cc[phy_id]; + struct cal_camerarx *cc = ctx->dev->cc[phy_id]; regmap_field_write(cc->phy.fields[F_CTRLCLKEN], 0); } @@ -552,10 +560,10 @@ static void camerarx_phy_disable(struct cal_ctx *ctx) /* * Camera Instance access block */ -static struct cc_data *cc_create(struct cal_dev *dev, unsigned int core) +static struct cal_camerarx *cc_create(struct cal_dev *dev, unsigned int core) { struct platform_device *pdev = dev->pdev; - struct cc_data *cc; + struct cal_camerarx *cc; int ret; cc = devm_kzalloc(&pdev->dev, sizeof(*cc), GFP_KERNEL); -- cgit v1.2.3 From d1739eabdd9600569ff2da170b3baf343263f952 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:46 +0200 Subject: media: ti-vpe: cal: Rename cal_csi2_phy to cal_camerarx_data The cal_csi2_phy contains platform data for the CAMERARX blocks. Rename it to cal_camerarx_data. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 621cc3f26762..faf18980656e 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -215,7 +215,7 @@ enum cal_camerarx_field { F_MAX_FIELDS, }; -struct cal_csi2_phy { +struct cal_camerarx_data { struct { unsigned int lsb; unsigned int msb; @@ -224,13 +224,12 @@ struct cal_csi2_phy { }; struct cal_data { + const struct cal_camerarx_data *camerarx; unsigned int num_csi2_phy; - const struct cal_csi2_phy *csi2_phy_core; - unsigned int flags; }; -static const struct cal_csi2_phy dra72x_cal_csi_phy[] = { +static const struct cal_camerarx_data dra72x_cal_camerarx[] = { { .fields = { [F_CTRLCLKEN] = { 10, 10 }, @@ -252,17 +251,17 @@ static const struct cal_csi2_phy dra72x_cal_csi_phy[] = { }; static const struct cal_data dra72x_cal_data = { - .csi2_phy_core = dra72x_cal_csi_phy, - .num_csi2_phy = ARRAY_SIZE(dra72x_cal_csi_phy), + .camerarx = dra72x_cal_camerarx, + .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), }; static const struct cal_data dra72x_es1_cal_data = { - .csi2_phy_core = dra72x_cal_csi_phy, - .num_csi2_phy = ARRAY_SIZE(dra72x_cal_csi_phy), + .camerarx = dra72x_cal_camerarx, + .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), .flags = DRA72_CAL_PRE_ES2_LDO_DISABLE, }; -static const struct cal_csi2_phy dra76x_cal_csi_phy[] = { +static const struct cal_camerarx_data dra76x_cal_csi_phy[] = { { .fields = { [F_CTRLCLKEN] = { 8, 8 }, @@ -284,11 +283,11 @@ static const struct cal_csi2_phy dra76x_cal_csi_phy[] = { }; static const struct cal_data dra76x_cal_data = { - .csi2_phy_core = dra76x_cal_csi_phy, + .camerarx = dra76x_cal_csi_phy, .num_csi2_phy = ARRAY_SIZE(dra76x_cal_csi_phy), }; -static const struct cal_csi2_phy am654_cal_csi_phy[] = { +static const struct cal_camerarx_data am654_cal_csi_phy[] = { { .fields = { [F_CTRLCLKEN] = { 15, 15 }, @@ -300,7 +299,7 @@ static const struct cal_csi2_phy am654_cal_csi_phy[] = { }; static const struct cal_data am654_cal_data = { - .csi2_phy_core = am654_cal_csi_phy, + .camerarx = am654_cal_csi_phy, .num_csi2_phy = ARRAY_SIZE(am654_cal_csi_phy), }; @@ -456,7 +455,7 @@ static u32 cal_data_get_phy_max_lanes(struct cal_ctx *ctx) struct cal_dev *dev = ctx->dev; u32 phy_id = ctx->csi2_port; - return dev->data->csi2_phy_core[phy_id].num_lanes; + return dev->data->camerarx[phy_id].num_lanes; } static u32 cal_data_get_num_csi2_phy(struct cal_dev *dev) @@ -468,19 +467,19 @@ static int cal_camerarx_regmap_init(struct cal_dev *dev, struct cal_camerarx *cc, unsigned int idx) { - const struct cal_csi2_phy *phy; + const struct cal_camerarx_data *phy_data; unsigned int i; if (!dev->data) return -EINVAL; - phy = &dev->data->csi2_phy_core[idx]; + phy_data = &dev->data->camerarx[idx]; for (i = 0; i < F_MAX_FIELDS; i++) { struct reg_field field = { .reg = dev->syscon_camerrx_offset, - .lsb = phy->fields[i].lsb, - .msb = phy->fields[i].msb, + .lsb = phy_data->fields[i].lsb, + .msb = phy_data->fields[i].msb, }; /* -- cgit v1.2.3 From 97cf202807c9154e6a84be4b53ce8c4228de2246 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:47 +0200 Subject: media: ti-vpe: cal: Name all cal_dev pointers consistently Name all variables pointing to a cal_dev instance 'cal', instead of a combination of 'caldev' and 'dev'. 'caldev' is only used in a few places and could have been renamed to 'dev', but 'dev' is confusing as it's also used for struct device instances. This generates lots of changes, but increases readability. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 368 ++++++++++++++++++------------------ 1 file changed, 186 insertions(+), 182 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index faf18980656e..f0e06a4699fc 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -53,12 +53,12 @@ static unsigned debug; module_param(debug, uint, 0644); MODULE_PARM_DESC(debug, "activates debug info"); -#define cal_dbg(level, caldev, fmt, arg...) \ - v4l2_dbg(level, debug, &caldev->v4l2_dev, fmt, ##arg) -#define cal_info(caldev, fmt, arg...) \ - v4l2_info(&caldev->v4l2_dev, fmt, ##arg) -#define cal_err(caldev, fmt, arg...) \ - v4l2_err(&caldev->v4l2_dev, fmt, ##arg) +#define cal_dbg(level, cal, fmt, arg...) \ + v4l2_dbg(level, debug, &cal->v4l2_dev, fmt, ##arg) +#define cal_info(cal, fmt, arg...) \ + v4l2_info(&cal->v4l2_dev, fmt, ##arg) +#define cal_err(cal, fmt, arg...) \ + v4l2_err(&cal->v4l2_dev, fmt, ##arg) #define ctx_dbg(level, ctx, fmt, arg...) \ v4l2_dbg(level, debug, &ctx->v4l2_dev, fmt, ##arg) @@ -309,9 +309,14 @@ static const struct cal_data am654_cal_data = { * DMA contexts. * * The cal_dev structure represents the whole subsystem, including the CAL and - * the CAMERARX instances. The cal_camerarx structure represents one CAMERARX - * instance. The cal_ctx structure represents the combination of one CSI-2 - * context, one processing context and one DMA context. + * the CAMERARX instances. Instances of struct cal_dev are named cal through the + * driver. + * + * The cal_camerarx structure represents one CAMERARX instance. + * + * The cal_ctx structure represents the combination of one CSI-2 context, one + * processing context and one DMA context. Instance of struct cal_ctx are named + * ctx through the driver. */ struct cal_camerarx { @@ -355,7 +360,7 @@ struct cal_ctx { struct v4l2_subdev *sensor; struct v4l2_fwnode_endpoint endpoint; - struct cal_dev *dev; + struct cal_dev *cal; struct cal_camerarx *cc; /* v4l2_ioctl mutex */ @@ -391,19 +396,19 @@ struct cal_ctx { bool dma_act; }; -static inline u32 reg_read_field(struct cal_dev *dev, u32 offset, u32 mask) +static inline u32 reg_read_field(struct cal_dev *cal, u32 offset, u32 mask) { - return FIELD_GET(mask, reg_read(dev, offset)); + return FIELD_GET(mask, reg_read(cal, offset)); } -static inline void reg_write_field(struct cal_dev *dev, u32 offset, u32 value, +static inline void reg_write_field(struct cal_dev *cal, u32 offset, u32 value, u32 mask) { - u32 val = reg_read(dev, offset); + u32 val = reg_read(cal, offset); val &= ~mask; val |= FIELD_PREP(mask, value); - reg_write(dev, offset, val); + reg_write(cal, offset, val); } static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx, @@ -452,32 +457,31 @@ static inline void set_field(u32 *valp, u32 field, u32 mask) static u32 cal_data_get_phy_max_lanes(struct cal_ctx *ctx) { - struct cal_dev *dev = ctx->dev; u32 phy_id = ctx->csi2_port; - return dev->data->camerarx[phy_id].num_lanes; + return ctx->cal->data->camerarx[phy_id].num_lanes; } -static u32 cal_data_get_num_csi2_phy(struct cal_dev *dev) +static u32 cal_data_get_num_csi2_phy(struct cal_dev *cal) { - return dev->data->num_csi2_phy; + return cal->data->num_csi2_phy; } -static int cal_camerarx_regmap_init(struct cal_dev *dev, +static int cal_camerarx_regmap_init(struct cal_dev *cal, struct cal_camerarx *cc, unsigned int idx) { const struct cal_camerarx_data *phy_data; unsigned int i; - if (!dev->data) + if (!cal->data) return -EINVAL; - phy_data = &dev->data->camerarx[idx]; + phy_data = &cal->data->camerarx[idx]; for (i = 0; i < F_MAX_FIELDS; i++) { struct reg_field field = { - .reg = dev->syscon_camerrx_offset, + .reg = cal->syscon_camerrx_offset, .lsb = phy_data->fields[i].lsb, .msb = phy_data->fields[i].msb, }; @@ -486,11 +490,11 @@ static int cal_camerarx_regmap_init(struct cal_dev *dev, * Here we update the reg offset with the * value found in DT */ - cc->phy.fields[i] = devm_regmap_field_alloc(&dev->pdev->dev, - dev->syscon_camerrx, + cc->phy.fields[i] = devm_regmap_field_alloc(&cal->pdev->dev, + cal->syscon_camerrx, field); if (IS_ERR(cc->phy.fields[i])) { - cal_err(dev, "Unable to allocate regmap fields\n"); + cal_err(cal, "Unable to allocate regmap fields\n"); return PTR_ERR(cc->phy.fields[i]); } } @@ -498,9 +502,9 @@ static int cal_camerarx_regmap_init(struct cal_dev *dev, return 0; } -static struct regmap *cal_get_camerarx_regmap(struct cal_dev *dev) +static struct regmap *cal_get_camerarx_regmap(struct cal_dev *cal) { - struct platform_device *pdev = dev->pdev; + struct platform_device *pdev = cal->pdev; struct regmap_config config = { }; struct regmap *regmap; void __iomem *base; @@ -510,11 +514,11 @@ static struct regmap *cal_get_camerarx_regmap(struct cal_dev *dev) "camerrx_control"); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) { - cal_err(dev, "failed to ioremap\n"); + cal_err(cal, "failed to ioremap\n"); return ERR_CAST(base); } - cal_dbg(1, dev, "ioresource %s at %pa - %pa\n", + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", res->name, &res->start, &res->end); config.reg_bits = 32; @@ -535,7 +539,7 @@ static struct regmap *cal_get_camerarx_regmap(struct cal_dev *dev) static void camerarx_phy_enable(struct cal_ctx *ctx) { u32 phy_id = ctx->csi2_port; - struct cal_camerarx *cc = ctx->dev->cc[phy_id]; + struct cal_camerarx *cc = ctx->cal->cc[phy_id]; u32 max_lanes; regmap_field_write(cc->phy.fields[F_CAMMODE], 0); @@ -551,7 +555,7 @@ static void camerarx_phy_enable(struct cal_ctx *ctx) static void camerarx_phy_disable(struct cal_ctx *ctx) { u32 phy_id = ctx->csi2_port; - struct cal_camerarx *cc = ctx->dev->cc[phy_id]; + struct cal_camerarx *cc = ctx->cal->cc[phy_id]; regmap_field_write(cc->phy.fields[F_CTRLCLKEN], 0); } @@ -559,9 +563,9 @@ static void camerarx_phy_disable(struct cal_ctx *ctx) /* * Camera Instance access block */ -static struct cal_camerarx *cc_create(struct cal_dev *dev, unsigned int core) +static struct cal_camerarx *cc_create(struct cal_dev *cal, unsigned int core) { - struct platform_device *pdev = dev->pdev; + struct platform_device *pdev = cal->pdev; struct cal_camerarx *cc; int ret; @@ -576,14 +580,14 @@ static struct cal_camerarx *cc_create(struct cal_dev *dev, unsigned int core) "cal_rx_core1"); cc->base = devm_ioremap_resource(&pdev->dev, cc->res); if (IS_ERR(cc->base)) { - cal_err(dev, "failed to ioremap\n"); + cal_err(cal, "failed to ioremap\n"); return ERR_CAST(cc->base); } - cal_dbg(1, dev, "ioresource %s at %pa - %pa\n", + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", cc->res->name, &cc->res->start, &cc->res->end); - ret = cal_camerarx_regmap_init(dev, cc, core); + ret = cal_camerarx_regmap_init(cal, cc, core); if (ret) return ERR_PTR(ret); @@ -593,17 +597,17 @@ static struct cal_camerarx *cc_create(struct cal_dev *dev, unsigned int core) /* * Get Revision and HW info */ -static void cal_get_hwinfo(struct cal_dev *dev) +static void cal_get_hwinfo(struct cal_dev *cal) { u32 revision; u32 hwinfo; - revision = reg_read(dev, CAL_HL_REVISION); - cal_dbg(3, dev, "CAL_HL_REVISION = 0x%08x (expecting 0x40000200)\n", + revision = reg_read(cal, CAL_HL_REVISION); + cal_dbg(3, cal, "CAL_HL_REVISION = 0x%08x (expecting 0x40000200)\n", revision); - hwinfo = reg_read(dev, CAL_HL_HWINFO); - cal_dbg(3, dev, "CAL_HL_HWINFO = 0x%08x (expecting 0xA3C90469)\n", + hwinfo = reg_read(cal, CAL_HL_HWINFO); + cal_dbg(3, cal, "CAL_HL_HWINFO = 0x%08x (expecting 0xA3C90469)\n", hwinfo); } @@ -626,38 +630,38 @@ static void cal_get_hwinfo(struct cal_dev *dev) * Core 0: 0x4845 B828 = 0x0000 0040 * Core 1: 0x4845 B928 = 0x0000 0040 */ -static void i913_errata(struct cal_dev *dev, unsigned int port) +static void i913_errata(struct cal_dev *cal, unsigned int port) { - u32 reg10 = reg_read(dev->cc[port], CAL_CSI2_PHY_REG10); + u32 reg10 = reg_read(cal->cc[port], CAL_CSI2_PHY_REG10); set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); - cal_dbg(1, dev, "CSI2_%d_REG10 = 0x%08x\n", port, reg10); - reg_write(dev->cc[port], CAL_CSI2_PHY_REG10, reg10); + cal_dbg(1, cal, "CSI2_%d_REG10 = 0x%08x\n", port, reg10); + reg_write(cal->cc[port], CAL_CSI2_PHY_REG10, reg10); } -static void cal_quickdump_regs(struct cal_dev *dev) +static void cal_quickdump_regs(struct cal_dev *cal) { - cal_info(dev, "CAL Registers @ 0x%pa:\n", &dev->res->start); + cal_info(cal, "CAL Registers @ 0x%pa:\n", &cal->res->start); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)dev->base, - resource_size(dev->res), false); + (__force const void *)cal->base, + resource_size(cal->res), false); - if (dev->ctx[0]) { - cal_info(dev, "CSI2 Core 0 Registers @ %pa:\n", - &dev->ctx[0]->cc->res->start); + if (cal->ctx[0]) { + cal_info(cal, "CSI2 Core 0 Registers @ %pa:\n", + &cal->ctx[0]->cc->res->start); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)dev->ctx[0]->cc->base, - resource_size(dev->ctx[0]->cc->res), + (__force const void *)cal->ctx[0]->cc->base, + resource_size(cal->ctx[0]->cc->res), false); } - if (dev->ctx[1]) { - cal_info(dev, "CSI2 Core 1 Registers @ %pa:\n", - &dev->ctx[1]->cc->res->start); + if (cal->ctx[1]) { + cal_info(cal, "CSI2 Core 1 Registers @ %pa:\n", + &cal->ctx[1]->cc->res->start); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)dev->ctx[1]->cc->base, - resource_size(dev->ctx[1]->cc->res), + (__force const void *)cal->ctx[1]->cc->base, + resource_size(cal->ctx[1]->cc->res), false); } } @@ -676,24 +680,24 @@ static void enable_irqs(struct cal_ctx *ctx) CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; /* Enable CIO error irqs */ - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(0), + reg_write(ctx->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_CIO_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), + reg_write(ctx->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), cio_err_mask); /* Always enable OCPO error */ - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); + reg_write(ctx->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); /* Enable IRQ_WDMA_END 0/1 */ val = 0; set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(1), val); + reg_write(ctx->cal, CAL_HL_IRQENABLE_SET(1), val); /* Enable IRQ_WDMA_START 0/1 */ val = 0; set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(2), val); + reg_write(ctx->cal, CAL_HL_IRQENABLE_SET(2), val); /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); + reg_write(ctx->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); } static void disable_irqs(struct cal_ctx *ctx) @@ -701,21 +705,21 @@ static void disable_irqs(struct cal_ctx *ctx) u32 val; /* Disable CIO error irqs */ - reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(0), + reg_write(ctx->cal, CAL_HL_IRQENABLE_CLR(0), CAL_HL_IRQ_CIO_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), + reg_write(ctx->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), 0); /* Disable IRQ_WDMA_END 0/1 */ val = 0; set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(1), val); + reg_write(ctx->cal, CAL_HL_IRQENABLE_CLR(1), val); /* Disable IRQ_WDMA_START 0/1 */ val = 0; set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(2), val); + reg_write(ctx->cal, CAL_HL_IRQENABLE_CLR(2), val); /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(0), 0); + reg_write(ctx->cal, CAL_CSI2_VC_IRQENABLE(0), 0); } static void csi2_cio_power(struct cal_ctx *ctx, bool enable) @@ -726,13 +730,13 @@ static void csi2_cio_power(struct cal_ctx *ctx, bool enable) target_state = enable ? CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON : CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF; - reg_write_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + reg_write_field(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), target_state, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK); for (i = 0; i < 10; i++) { u32 current_state; - current_state = reg_read_field(ctx->dev, + current_state = reg_read_field(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK); @@ -822,12 +826,12 @@ static void csi2_phy_init(struct cal_ctx *ctx) camerarx_phy_enable(ctx); /* 2. Reset complex IO - Do not wait for reset completion */ - reg_write_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + reg_write_field(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port))); + reg_read(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port))); /* Dummy read to allow SCP reset to complete */ reg_read(ctx->cc, CAL_CSI2_PHY_REG0); @@ -844,23 +848,23 @@ static void csi2_phy_init(struct cal_ctx *ctx) * Stop-state-timeout must be more than 100us as per CSI2 spec, so we * calculate a timeout that's 100us (rounding up). */ - sscounter = DIV_ROUND_UP(clk_get_rate(ctx->dev->fclk), 10000 * 16 * 4); + sscounter = DIV_ROUND_UP(clk_get_rate(ctx->cal->fclk), 10000 * 16 * 4); - val = reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port)); + val = reg_read(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port)); set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK); set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK); set_field(&val, sscounter, CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK); - reg_write(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), val); + reg_write(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port), val); ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port))); + reg_read(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port))); /* 4. Force FORCERXMODE */ - reg_write_field(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), + reg_write_field(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port), 1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK); ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port))); + reg_read(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port))); /* E. Power up the PHY using the complex IO */ csi2_cio_power(ctx, true); @@ -872,7 +876,7 @@ static void csi2_wait_complexio_reset(struct cal_ctx *ctx) timeout = jiffies + msecs_to_jiffies(750); while (time_before(jiffies, timeout)) { - if (reg_read_field(ctx->dev, + if (reg_read_field(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) @@ -880,7 +884,7 @@ static void csi2_wait_complexio_reset(struct cal_ctx *ctx) usleep_range(500, 5000); } - if (reg_read_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + if (reg_read_field(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) != CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) ctx_err(ctx, "Timeout waiting for Complex IO reset done\n"); @@ -892,14 +896,14 @@ static void csi2_wait_stop_state(struct cal_ctx *ctx) timeout = jiffies + msecs_to_jiffies(750); while (time_before(jiffies, timeout)) { - if (reg_read_field(ctx->dev, + if (reg_read_field(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port), CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0) break; usleep_range(500, 5000); } - if (reg_read_field(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), + if (reg_read_field(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port), CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0) ctx_err(ctx, "Timeout waiting for stop state\n"); } @@ -932,13 +936,13 @@ static void csi2_phy_deinit(struct cal_ctx *ctx) csi2_cio_power(ctx, false); /* Assert Comple IO Reset */ - reg_write_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + reg_write_field(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); /* Wait for power down completion */ for (i = 0; i < 10; i++) { - if (reg_read_field(ctx->dev, + if (reg_read_field(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING) @@ -947,7 +951,7 @@ static void csi2_phy_deinit(struct cal_ctx *ctx) } ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)), i, + reg_read(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)), i, (i >= 10) ? "(timeout)" : ""); /* Disable the phy */ @@ -956,7 +960,7 @@ static void csi2_phy_deinit(struct cal_ctx *ctx) static void csi2_lane_config(struct cal_ctx *ctx) { - u32 val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)); + u32 val = reg_read(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)); u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = @@ -977,21 +981,21 @@ static void csi2_lane_config(struct cal_ctx *ctx) polarity_mask); } - reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val); + reg_write(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val); ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", ctx->csi2_port, val); } static void csi2_ppi_enable(struct cal_ctx *ctx) { - reg_write(ctx->dev, CAL_CSI2_PPI_CTRL(ctx->csi2_port), BIT(3)); - reg_write_field(ctx->dev, CAL_CSI2_PPI_CTRL(ctx->csi2_port), + reg_write(ctx->cal, CAL_CSI2_PPI_CTRL(ctx->csi2_port), BIT(3)); + reg_write_field(ctx->cal, CAL_CSI2_PPI_CTRL(ctx->csi2_port), 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } static void csi2_ppi_disable(struct cal_ctx *ctx) { - reg_write_field(ctx->dev, CAL_CSI2_PPI_CTRL(ctx->csi2_port), + reg_write_field(ctx->cal, CAL_CSI2_PPI_CTRL(ctx->csi2_port), 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } @@ -999,7 +1003,7 @@ static void csi2_ctx_config(struct cal_ctx *ctx) { u32 val; - val = reg_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port)); + val = reg_read(ctx->cal, CAL_CSI2_CTX0(ctx->csi2_port)); set_field(&val, ctx->cport, CAL_CSI2_CTX_CPORT_MASK); /* * DT type: MIPI CSI-2 Specs @@ -1016,9 +1020,9 @@ static void csi2_ctx_config(struct cal_ctx *ctx) set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK); set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE, CAL_CSI2_CTX_PACK_MODE_MASK); - reg_write(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port), val); + reg_write(ctx->cal, CAL_CSI2_CTX0(ctx->csi2_port), val); ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port))); + reg_read(ctx->cal, CAL_CSI2_CTX0(ctx->csi2_port))); } static void pix_proc_config(struct cal_ctx *ctx) @@ -1052,7 +1056,7 @@ static void pix_proc_config(struct cal_ctx *ctx) * * Instead of failing here just use 8 bpp as a default. */ - dev_warn_once(&ctx->dev->pdev->dev, + dev_warn_once(&ctx->cal->pdev->dev, "%s:%d:%s: bpp:%d unsupported! Overwritten with 8.\n", __FILE__, __LINE__, __func__, ctx->fmt->bpp); extract = CAL_PIX_PROC_EXTRACT_B8; @@ -1060,16 +1064,16 @@ static void pix_proc_config(struct cal_ctx *ctx) break; } - val = reg_read(ctx->dev, CAL_PIX_PROC(ctx->csi2_port)); + val = reg_read(ctx->cal, CAL_PIX_PROC(ctx->csi2_port)); set_field(&val, extract, CAL_PIX_PROC_EXTRACT_MASK); set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK); set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK); set_field(&val, pack, CAL_PIX_PROC_PACK_MASK); set_field(&val, ctx->cport, CAL_PIX_PROC_CPORT_MASK); set_field(&val, 1, CAL_PIX_PROC_EN_MASK); - reg_write(ctx->dev, CAL_PIX_PROC(ctx->csi2_port), val); + reg_write(ctx->cal, CAL_PIX_PROC(ctx->csi2_port), val); ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_PIX_PROC(ctx->csi2_port))); + reg_read(ctx->cal, CAL_PIX_PROC(ctx->csi2_port))); } static void cal_wr_dma_config(struct cal_ctx *ctx, @@ -1077,7 +1081,7 @@ static void cal_wr_dma_config(struct cal_ctx *ctx, { u32 val; - val = reg_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port)); + val = reg_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->csi2_port)); set_field(&val, ctx->cport, CAL_WR_DMA_CTRL_CPORT_MASK); set_field(&val, height, CAL_WR_DMA_CTRL_YSIZE_MASK); set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT, @@ -1087,22 +1091,22 @@ static void cal_wr_dma_config(struct cal_ctx *ctx, set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR, CAL_WR_DMA_CTRL_PATTERN_MASK); set_field(&val, 1, CAL_WR_DMA_CTRL_STALL_RD_MASK); - reg_write(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port), val); + reg_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->csi2_port), val); ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port))); + reg_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->csi2_port))); /* * width/16 not sure but giving it a whirl. * zero does not work right */ - reg_write_field(ctx->dev, + reg_write_field(ctx->cal, CAL_WR_DMA_OFST(ctx->csi2_port), (width / 16), CAL_WR_DMA_OFST_MASK); ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_WR_DMA_OFST(ctx->csi2_port))); + reg_read(ctx->cal, CAL_WR_DMA_OFST(ctx->csi2_port))); - val = reg_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port)); + val = reg_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->csi2_port)); /* 64 bit word means no skipping */ set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK); /* @@ -1111,24 +1115,24 @@ static void cal_wr_dma_config(struct cal_ctx *ctx, * is detected automagically */ set_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK); - reg_write(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port), val); + reg_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->csi2_port), val); ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port))); + reg_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->csi2_port))); - val = reg_read(ctx->dev, CAL_CTRL); + val = reg_read(ctx->cal, CAL_CTRL); set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, CAL_CTRL_BURSTSIZE_MASK); set_field(&val, 0xF, CAL_CTRL_TAGCNT_MASK); set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED, CAL_CTRL_POSTED_WRITES_MASK); set_field(&val, 0xFF, CAL_CTRL_MFLAGL_MASK); set_field(&val, 0xFF, CAL_CTRL_MFLAGH_MASK); - reg_write(ctx->dev, CAL_CTRL, val); - ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", reg_read(ctx->dev, CAL_CTRL)); + reg_write(ctx->cal, CAL_CTRL, val); + ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", reg_read(ctx->cal, CAL_CTRL)); } static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) { - reg_write(ctx->dev, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr); + reg_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr); } static int cal_get_external_info(struct cal_ctx *ctx) @@ -1182,45 +1186,45 @@ static inline void cal_process_buffer_complete(struct cal_ctx *ctx) static irqreturn_t cal_irq(int irq_cal, void *data) { - struct cal_dev *dev = data; + struct cal_dev *cal = data; struct cal_ctx *ctx; struct cal_dmaqueue *dma_q; u32 status; - status = reg_read(dev, CAL_HL_IRQSTATUS(0)); + status = reg_read(cal, CAL_HL_IRQSTATUS(0)); if (status) { unsigned int i; - reg_write(dev, CAL_HL_IRQSTATUS(0), status); + reg_write(cal, CAL_HL_IRQSTATUS(0), status); if (status & CAL_HL_IRQ_OCPO_ERR_MASK) - dev_err_ratelimited(&dev->pdev->dev, "OCPO ERROR\n"); + dev_err_ratelimited(&cal->pdev->dev, "OCPO ERROR\n"); for (i = 0; i < 2; ++i) { if (status & CAL_HL_IRQ_CIO_MASK(i)) { - u32 cio_stat = reg_read(dev, + u32 cio_stat = reg_read(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); - dev_err_ratelimited(&dev->pdev->dev, + dev_err_ratelimited(&cal->pdev->dev, "CIO%u error: %#08x\n", i, cio_stat); - reg_write(dev, CAL_CSI2_COMPLEXIO_IRQSTATUS(i), + reg_write(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i), cio_stat); } } } /* Check which DMA just finished */ - status = reg_read(dev, CAL_HL_IRQSTATUS(1)); + status = reg_read(cal, CAL_HL_IRQSTATUS(1)); if (status) { unsigned int i; /* Clear Interrupt status */ - reg_write(dev, CAL_HL_IRQSTATUS(1), status); + reg_write(cal, CAL_HL_IRQSTATUS(1), status); for (i = 0; i < 2; ++i) { if (isportirqset(status, i)) { - ctx = dev->ctx[i]; + ctx = cal->ctx[i]; spin_lock(&ctx->slock); ctx->dma_act = false; @@ -1234,16 +1238,16 @@ static irqreturn_t cal_irq(int irq_cal, void *data) } /* Check which DMA just started */ - status = reg_read(dev, CAL_HL_IRQSTATUS(2)); + status = reg_read(cal, CAL_HL_IRQSTATUS(2)); if (status) { unsigned int i; /* Clear Interrupt status */ - reg_write(dev, CAL_HL_IRQSTATUS(2), status); + reg_write(cal, CAL_HL_IRQSTATUS(2), status); for (i = 0; i < 2; ++i) { if (isportirqset(status, i)) { - ctx = dev->ctx[i]; + ctx = cal->ctx[i]; dma_q = &ctx->vidq; spin_lock(&ctx->slock); @@ -1667,7 +1671,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) goto err; } - pm_runtime_get_sync(&ctx->dev->pdev->dev); + pm_runtime_get_sync(&ctx->cal->pdev->dev); csi2_ctx_config(ctx); pix_proc_config(ctx); @@ -1682,7 +1686,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) if (ret) { v4l2_subdev_call(ctx->sensor, core, s_power, 0); ctx_err(ctx, "stream on failed in subdev\n"); - pm_runtime_put_sync(&ctx->dev->pdev->dev); + pm_runtime_put_sync(&ctx->cal->pdev->dev); goto err; } @@ -1691,7 +1695,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) csi2_ppi_enable(ctx); if (debug >= 4) - cal_quickdump_regs(ctx->dev); + cal_quickdump_regs(ctx->cal); return 0; @@ -1762,7 +1766,7 @@ static void cal_stop_streaming(struct vb2_queue *vq) ctx->next_frm = NULL; spin_unlock_irqrestore(&ctx->slock, flags); - pm_runtime_put_sync(&ctx->dev->pdev->dev); + pm_runtime_put_sync(&ctx->cal->pdev->dev); } static const struct vb2_ops cal_video_qops = { @@ -2037,7 +2041,7 @@ of_get_next_endpoint(const struct device_node *parent, static int of_cal_create_instance(struct cal_ctx *ctx, int inst) { - struct platform_device *pdev = ctx->dev->pdev; + struct platform_device *pdev = ctx->cal->pdev; struct device_node *ep_node, *port, *sensor_node, *parent; struct v4l2_fwnode_endpoint *endpoint; struct v4l2_async_subdev *asd; @@ -2160,22 +2164,22 @@ cleanup_exit: return ret; } -static struct cal_ctx *cal_create_instance(struct cal_dev *dev, int inst) +static struct cal_ctx *cal_create_instance(struct cal_dev *cal, int inst) { struct cal_ctx *ctx; struct v4l2_ctrl_handler *hdl; int ret; - ctx = devm_kzalloc(&dev->pdev->dev, sizeof(*ctx), GFP_KERNEL); + ctx = devm_kzalloc(&cal->pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return NULL; /* save the cal_dev * for future ref */ - ctx->dev = dev; + ctx->cal = cal; snprintf(ctx->v4l2_dev.name, sizeof(ctx->v4l2_dev.name), "%s-%03d", CAL_MODULE_NAME, inst); - ret = v4l2_device_register(&dev->pdev->dev, &ctx->v4l2_dev); + ret = v4l2_device_register(&cal->pdev->dev, &ctx->v4l2_dev); if (ret) goto err_exit; @@ -2188,7 +2192,7 @@ static struct cal_ctx *cal_create_instance(struct cal_dev *dev, int inst) ctx->v4l2_dev.ctrl_handler = hdl; /* Make sure Camera Core H/W register area is available */ - ctx->cc = dev->cc[inst]; + ctx->cc = cal->cc[inst]; /* Store the instance id */ ctx->csi2_port = inst; @@ -2232,7 +2236,7 @@ MODULE_DEVICE_TABLE(of, cal_of_match); static int cal_probe(struct platform_device *pdev) { - struct cal_dev *dev; + struct cal_dev *cal; struct cal_ctx *ctx; struct device_node *parent = pdev->dev.of_node; struct regmap *syscon_camerrx; @@ -2241,27 +2245,27 @@ static int cal_probe(struct platform_device *pdev) int ret; int irq; - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); - if (!dev) + cal = devm_kzalloc(&pdev->dev, sizeof(*cal), GFP_KERNEL); + if (!cal) return -ENOMEM; - dev->data = of_device_get_match_data(&pdev->dev); - if (!dev->data) { + cal->data = of_device_get_match_data(&pdev->dev); + if (!cal->data) { dev_err(&pdev->dev, "Could not get feature data based on compatible version\n"); return -ENODEV; } /* set pseudo v4l2 device name so we can use v4l2_printk */ - strscpy(dev->v4l2_dev.name, CAL_MODULE_NAME, - sizeof(dev->v4l2_dev.name)); + strscpy(cal->v4l2_dev.name, CAL_MODULE_NAME, + sizeof(cal->v4l2_dev.name)); /* save pdev pointer */ - dev->pdev = pdev; + cal->pdev = pdev; - dev->fclk = devm_clk_get(&pdev->dev, "fck"); - if (IS_ERR(dev->fclk)) { + cal->fclk = devm_clk_get(&pdev->dev, "fck"); + if (IS_ERR(cal->fclk)) { dev_err(&pdev->dev, "cannot get CAL fclk\n"); - return PTR_ERR(dev->fclk); + return PTR_ERR(cal->fclk); } syscon_camerrx = syscon_regmap_lookup_by_phandle(parent, @@ -2279,7 +2283,7 @@ static int cal_probe(struct platform_device *pdev) * If syscon entry is not present then check if the * camerrx_control resource is present. */ - syscon_camerrx = cal_get_camerarx_regmap(dev); + syscon_camerrx = cal_get_camerarx_regmap(cal); if (IS_ERR(syscon_camerrx)) { dev_err(&pdev->dev, "failed to get camerrx_control regmap\n"); return PTR_ERR(syscon_camerrx); @@ -2290,47 +2294,47 @@ static int cal_probe(struct platform_device *pdev) syscon_camerrx_offset = 0; } - dev->syscon_camerrx = syscon_camerrx; - dev->syscon_camerrx_offset = syscon_camerrx_offset; + cal->syscon_camerrx = syscon_camerrx; + cal->syscon_camerrx_offset = syscon_camerrx_offset; - dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + cal->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cal_top"); - dev->base = devm_ioremap_resource(&pdev->dev, dev->res); - if (IS_ERR(dev->base)) - return PTR_ERR(dev->base); + cal->base = devm_ioremap_resource(&pdev->dev, cal->res); + if (IS_ERR(cal->base)) + return PTR_ERR(cal->base); - cal_dbg(1, dev, "ioresource %s at %pa - %pa\n", - dev->res->name, &dev->res->start, &dev->res->end); + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", + cal->res->name, &cal->res->start, &cal->res->end); irq = platform_get_irq(pdev, 0); - cal_dbg(1, dev, "got irq# %d\n", irq); + cal_dbg(1, cal, "got irq# %d\n", irq); ret = devm_request_irq(&pdev->dev, irq, cal_irq, 0, CAL_MODULE_NAME, - dev); + cal); if (ret) return ret; - platform_set_drvdata(pdev, dev); + platform_set_drvdata(pdev, cal); - dev->cc[0] = cc_create(dev, 0); - if (IS_ERR(dev->cc[0])) - return PTR_ERR(dev->cc[0]); + cal->cc[0] = cc_create(cal, 0); + if (IS_ERR(cal->cc[0])) + return PTR_ERR(cal->cc[0]); - if (cal_data_get_num_csi2_phy(dev) > 1) { - dev->cc[1] = cc_create(dev, 1); - if (IS_ERR(dev->cc[1])) - return PTR_ERR(dev->cc[1]); + if (cal_data_get_num_csi2_phy(cal) > 1) { + cal->cc[1] = cc_create(cal, 1); + if (IS_ERR(cal->cc[1])) + return PTR_ERR(cal->cc[1]); } else { - dev->cc[1] = NULL; + cal->cc[1] = NULL; } - dev->ctx[0] = NULL; - dev->ctx[1] = NULL; + cal->ctx[0] = NULL; + cal->ctx[1] = NULL; - dev->ctx[0] = cal_create_instance(dev, 0); - if (cal_data_get_num_csi2_phy(dev) > 1) - dev->ctx[1] = cal_create_instance(dev, 1); - if (!dev->ctx[0] && !dev->ctx[1]) { - cal_err(dev, "Neither port is configured, no point in staying up\n"); + cal->ctx[0] = cal_create_instance(cal, 0); + if (cal_data_get_num_csi2_phy(cal) > 1) + cal->ctx[1] = cal_create_instance(cal, 1); + if (!cal->ctx[0] && !cal->ctx[1]) { + cal_err(cal, "Neither port is configured, no point in staying up\n"); return -ENODEV; } @@ -2343,7 +2347,7 @@ static int cal_probe(struct platform_device *pdev) goto runtime_disable; /* Just check we can actually access the module */ - cal_get_hwinfo(dev); + cal_get_hwinfo(cal); pm_runtime_put_sync(&pdev->dev); @@ -2354,7 +2358,7 @@ runtime_disable: pm_runtime_disable(&pdev->dev); for (i = 0; i < CAL_NUM_CONTEXT; i++) { - ctx = dev->ctx[i]; + ctx = cal->ctx[i]; if (ctx) { v4l2_async_notifier_unregister(&ctx->notifier); v4l2_async_notifier_cleanup(&ctx->notifier); @@ -2368,16 +2372,16 @@ runtime_disable: static int cal_remove(struct platform_device *pdev) { - struct cal_dev *dev = platform_get_drvdata(pdev); + struct cal_dev *cal = platform_get_drvdata(pdev); struct cal_ctx *ctx; unsigned int i; - cal_dbg(1, dev, "Removing %s\n", CAL_MODULE_NAME); + cal_dbg(1, cal, "Removing %s\n", CAL_MODULE_NAME); pm_runtime_get_sync(&pdev->dev); for (i = 0; i < CAL_NUM_CONTEXT; i++) { - ctx = dev->ctx[i]; + ctx = cal->ctx[i]; if (ctx) { ctx_dbg(1, ctx, "unregistering %s\n", video_device_node_name(&ctx->vdev)); @@ -2400,15 +2404,15 @@ static int cal_remove(struct platform_device *pdev) static int cal_runtime_resume(struct device *dev) { - struct cal_dev *caldev = dev_get_drvdata(dev); + struct cal_dev *cal = dev_get_drvdata(dev); - if (caldev->data->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) { + if (cal->data->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) { /* * Apply errata on both port everytime we (re-)enable * the clock */ - i913_errata(caldev, 0); - i913_errata(caldev, 1); + i913_errata(cal, 0); + i913_errata(cal, 1); } return 0; -- cgit v1.2.3 From d8444ea02f3bf366de6eee1fa9359e1772ad24a4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:48 +0200 Subject: media: ti-vpe: cal: Name all cal_camerarx pointers consistently Name all variables that point to a cal_camerax instance 'phy' instead of 'cc'. The name 'cc' refers to Camera Core, but is not commonly used in the driver or in datasheets. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 102 ++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 51 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index f0e06a4699fc..d376cdd32f72 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -312,7 +312,8 @@ static const struct cal_data am654_cal_data = { * the CAMERARX instances. Instances of struct cal_dev are named cal through the * driver. * - * The cal_camerarx structure represents one CAMERARX instance. + * The cal_camerarx structure represents one CAMERARX instance. Instances of + * cal_camerarx are named phy through the driver. * * The cal_ctx structure represents the combination of one CSI-2 context, one * processing context and one DMA context. Instance of struct cal_ctx are named @@ -344,7 +345,7 @@ struct cal_dev { u32 syscon_camerrx_offset; /* Camera Core Module handle */ - struct cal_camerarx *cc[CAL_NUM_CSI2_PORTS]; + struct cal_camerarx *phy[CAL_NUM_CSI2_PORTS]; struct cal_ctx *ctx[CAL_NUM_CONTEXT]; }; @@ -361,7 +362,7 @@ struct cal_ctx { struct v4l2_fwnode_endpoint endpoint; struct cal_dev *cal; - struct cal_camerarx *cc; + struct cal_camerarx *phy; /* v4l2_ioctl mutex */ struct mutex mutex; @@ -468,7 +469,7 @@ static u32 cal_data_get_num_csi2_phy(struct cal_dev *cal) } static int cal_camerarx_regmap_init(struct cal_dev *cal, - struct cal_camerarx *cc, + struct cal_camerarx *phy, unsigned int idx) { const struct cal_camerarx_data *phy_data; @@ -490,12 +491,12 @@ static int cal_camerarx_regmap_init(struct cal_dev *cal, * Here we update the reg offset with the * value found in DT */ - cc->phy.fields[i] = devm_regmap_field_alloc(&cal->pdev->dev, - cal->syscon_camerrx, - field); - if (IS_ERR(cc->phy.fields[i])) { + phy->phy.fields[i] = devm_regmap_field_alloc(&cal->pdev->dev, + cal->syscon_camerrx, + field); + if (IS_ERR(phy->phy.fields[i])) { cal_err(cal, "Unable to allocate regmap fields\n"); - return PTR_ERR(cc->phy.fields[i]); + return PTR_ERR(phy->phy.fields[i]); } } @@ -539,25 +540,25 @@ static struct regmap *cal_get_camerarx_regmap(struct cal_dev *cal) static void camerarx_phy_enable(struct cal_ctx *ctx) { u32 phy_id = ctx->csi2_port; - struct cal_camerarx *cc = ctx->cal->cc[phy_id]; + struct cal_camerarx *phy = ctx->cal->phy[phy_id]; u32 max_lanes; - regmap_field_write(cc->phy.fields[F_CAMMODE], 0); + regmap_field_write(phy->phy.fields[F_CAMMODE], 0); /* Always enable all lanes at the phy control level */ max_lanes = (1 << cal_data_get_phy_max_lanes(ctx)) - 1; - regmap_field_write(cc->phy.fields[F_LANEENABLE], max_lanes); + regmap_field_write(phy->phy.fields[F_LANEENABLE], max_lanes); /* F_CSI_MODE is not present on every architecture */ - if (cc->phy.fields[F_CSI_MODE]) - regmap_field_write(cc->phy.fields[F_CSI_MODE], 1); - regmap_field_write(cc->phy.fields[F_CTRLCLKEN], 1); + if (phy->phy.fields[F_CSI_MODE]) + regmap_field_write(phy->phy.fields[F_CSI_MODE], 1); + regmap_field_write(phy->phy.fields[F_CTRLCLKEN], 1); } static void camerarx_phy_disable(struct cal_ctx *ctx) { u32 phy_id = ctx->csi2_port; - struct cal_camerarx *cc = ctx->cal->cc[phy_id]; + struct cal_camerarx *phy = ctx->cal->phy[phy_id]; - regmap_field_write(cc->phy.fields[F_CTRLCLKEN], 0); + regmap_field_write(phy->phy.fields[F_CTRLCLKEN], 0); } /* @@ -566,32 +567,31 @@ static void camerarx_phy_disable(struct cal_ctx *ctx) static struct cal_camerarx *cc_create(struct cal_dev *cal, unsigned int core) { struct platform_device *pdev = cal->pdev; - struct cal_camerarx *cc; + struct cal_camerarx *phy; int ret; - cc = devm_kzalloc(&pdev->dev, sizeof(*cc), GFP_KERNEL); - if (!cc) + phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); + if (!phy) return ERR_PTR(-ENOMEM); - cc->res = platform_get_resource_byname(pdev, - IORESOURCE_MEM, - (core == 0) ? + phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + (core == 0) ? "cal_rx_core0" : "cal_rx_core1"); - cc->base = devm_ioremap_resource(&pdev->dev, cc->res); - if (IS_ERR(cc->base)) { + phy->base = devm_ioremap_resource(&pdev->dev, phy->res); + if (IS_ERR(phy->base)) { cal_err(cal, "failed to ioremap\n"); - return ERR_CAST(cc->base); + return ERR_CAST(phy->base); } cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", - cc->res->name, &cc->res->start, &cc->res->end); + phy->res->name, &phy->res->start, &phy->res->end); - ret = cal_camerarx_regmap_init(cal, cc, core); + ret = cal_camerarx_regmap_init(cal, phy, core); if (ret) return ERR_PTR(ret); - return cc; + return phy; } /* @@ -632,12 +632,12 @@ static void cal_get_hwinfo(struct cal_dev *cal) */ static void i913_errata(struct cal_dev *cal, unsigned int port) { - u32 reg10 = reg_read(cal->cc[port], CAL_CSI2_PHY_REG10); + u32 reg10 = reg_read(cal->phy[port], CAL_CSI2_PHY_REG10); set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); cal_dbg(1, cal, "CSI2_%d_REG10 = 0x%08x\n", port, reg10); - reg_write(cal->cc[port], CAL_CSI2_PHY_REG10, reg10); + reg_write(cal->phy[port], CAL_CSI2_PHY_REG10, reg10); } static void cal_quickdump_regs(struct cal_dev *cal) @@ -649,19 +649,19 @@ static void cal_quickdump_regs(struct cal_dev *cal) if (cal->ctx[0]) { cal_info(cal, "CSI2 Core 0 Registers @ %pa:\n", - &cal->ctx[0]->cc->res->start); + &cal->ctx[0]->phy->res->start); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)cal->ctx[0]->cc->base, - resource_size(cal->ctx[0]->cc->res), + (__force const void *)cal->ctx[0]->phy->base, + resource_size(cal->ctx[0]->phy->res), false); } if (cal->ctx[1]) { cal_info(cal, "CSI2 Core 1 Registers @ %pa:\n", - &cal->ctx[1]->cc->res->start); + &cal->ctx[1]->phy->res->start); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)cal->ctx[1]->cc->base, - resource_size(cal->ctx[1]->cc->res), + (__force const void *)cal->ctx[1]->phy->base, + resource_size(cal->ctx[1]->phy->res), false); } } @@ -781,23 +781,23 @@ static void csi2_phy_config(struct cal_ctx *ctx) ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; ctx_dbg(1, ctx, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); - reg0 = reg_read(ctx->cc, CAL_CSI2_PHY_REG0); + reg0 = reg_read(ctx->phy, CAL_CSI2_PHY_REG0); set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); ctx_dbg(1, ctx, "CSI2_%d_REG0 = 0x%08x\n", ctx->csi2_port, reg0); - reg_write(ctx->cc, CAL_CSI2_PHY_REG0, reg0); + reg_write(ctx->phy, CAL_CSI2_PHY_REG0, reg0); - reg1 = reg_read(ctx->cc, CAL_CSI2_PHY_REG1); + reg1 = reg_read(ctx->phy, CAL_CSI2_PHY_REG1); set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); set_field(®1, TCLK_MISS, CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x\n", ctx->csi2_port, reg1); - reg_write(ctx->cc, CAL_CSI2_PHY_REG1, reg1); + reg_write(ctx->phy, CAL_CSI2_PHY_REG1, reg1); } static void csi2_phy_init(struct cal_ctx *ctx) @@ -834,7 +834,7 @@ static void csi2_phy_init(struct cal_ctx *ctx) reg_read(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port))); /* Dummy read to allow SCP reset to complete */ - reg_read(ctx->cc, CAL_CSI2_PHY_REG0); + reg_read(ctx->phy, CAL_CSI2_PHY_REG0); /* 3.A. Program Phy Timing Parameters */ csi2_phy_config(ctx); @@ -926,7 +926,7 @@ static void csi2_wait_for_phy(struct cal_ctx *ctx) csi2_wait_stop_state(ctx); ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x (Bit(31,28) should be set!)\n", - ctx->csi2_port, reg_read(ctx->cc, CAL_CSI2_PHY_REG1)); + ctx->csi2_port, reg_read(ctx->phy, CAL_CSI2_PHY_REG1)); } static void csi2_phy_deinit(struct cal_ctx *ctx) @@ -2192,7 +2192,7 @@ static struct cal_ctx *cal_create_instance(struct cal_dev *cal, int inst) ctx->v4l2_dev.ctrl_handler = hdl; /* Make sure Camera Core H/W register area is available */ - ctx->cc = cal->cc[inst]; + ctx->phy = cal->phy[inst]; /* Store the instance id */ ctx->csi2_port = inst; @@ -2315,16 +2315,16 @@ static int cal_probe(struct platform_device *pdev) platform_set_drvdata(pdev, cal); - cal->cc[0] = cc_create(cal, 0); - if (IS_ERR(cal->cc[0])) - return PTR_ERR(cal->cc[0]); + cal->phy[0] = cc_create(cal, 0); + if (IS_ERR(cal->phy[0])) + return PTR_ERR(cal->phy[0]); if (cal_data_get_num_csi2_phy(cal) > 1) { - cal->cc[1] = cc_create(cal, 1); - if (IS_ERR(cal->cc[1])) - return PTR_ERR(cal->cc[1]); + cal->phy[1] = cc_create(cal, 1); + if (IS_ERR(cal->phy[1])) + return PTR_ERR(cal->phy[1]); } else { - cal->cc[1] = NULL; + cal->phy[1] = NULL; } cal->ctx[0] = NULL; -- cgit v1.2.3 From be28027075adb8a940f340a2fe4e5ce18b500ed4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:49 +0200 Subject: media: ti-vpe: cal: Remove internal phy structure from cal_camerarx The cal_camerarx structure describes the PHY, there's no need for an internal structure named phy. Removed that level of indirection. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index d376cdd32f72..471e2581b28b 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -324,10 +324,7 @@ struct cal_camerarx { void __iomem *base; struct resource *res; struct platform_device *pdev; - - struct { - struct regmap_field *fields[F_MAX_FIELDS]; - } phy; + struct regmap_field *fields[F_MAX_FIELDS]; }; struct cal_dev { @@ -491,12 +488,12 @@ static int cal_camerarx_regmap_init(struct cal_dev *cal, * Here we update the reg offset with the * value found in DT */ - phy->phy.fields[i] = devm_regmap_field_alloc(&cal->pdev->dev, - cal->syscon_camerrx, - field); - if (IS_ERR(phy->phy.fields[i])) { + phy->fields[i] = devm_regmap_field_alloc(&cal->pdev->dev, + cal->syscon_camerrx, + field); + if (IS_ERR(phy->fields[i])) { cal_err(cal, "Unable to allocate regmap fields\n"); - return PTR_ERR(phy->phy.fields[i]); + return PTR_ERR(phy->fields[i]); } } @@ -543,14 +540,14 @@ static void camerarx_phy_enable(struct cal_ctx *ctx) struct cal_camerarx *phy = ctx->cal->phy[phy_id]; u32 max_lanes; - regmap_field_write(phy->phy.fields[F_CAMMODE], 0); + regmap_field_write(phy->fields[F_CAMMODE], 0); /* Always enable all lanes at the phy control level */ max_lanes = (1 << cal_data_get_phy_max_lanes(ctx)) - 1; - regmap_field_write(phy->phy.fields[F_LANEENABLE], max_lanes); + regmap_field_write(phy->fields[F_LANEENABLE], max_lanes); /* F_CSI_MODE is not present on every architecture */ - if (phy->phy.fields[F_CSI_MODE]) - regmap_field_write(phy->phy.fields[F_CSI_MODE], 1); - regmap_field_write(phy->phy.fields[F_CTRLCLKEN], 1); + if (phy->fields[F_CSI_MODE]) + regmap_field_write(phy->fields[F_CSI_MODE], 1); + regmap_field_write(phy->fields[F_CTRLCLKEN], 1); } static void camerarx_phy_disable(struct cal_ctx *ctx) @@ -558,7 +555,7 @@ static void camerarx_phy_disable(struct cal_ctx *ctx) u32 phy_id = ctx->csi2_port; struct cal_camerarx *phy = ctx->cal->phy[phy_id]; - regmap_field_write(phy->phy.fields[F_CTRLCLKEN], 0); + regmap_field_write(phy->fields[F_CTRLCLKEN], 0); } /* -- cgit v1.2.3 From d01a7fea9d7e48eefe1b85a8706fe4bed06589db Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:50 +0200 Subject: media: ti-vpe: cal: Store instance ID and cal pointer in cal_camerarx Store a pointer to the cal_dev and the cal_camerarx instance number in the cal_camerarx structure. This prepares for passing a cal_camerarx pointer instead of a cal_ctx pointer to multiple functions that deal with the CAMERARX. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 471e2581b28b..ff9bc3ae58ba 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -325,6 +325,9 @@ struct cal_camerarx { struct resource *res; struct platform_device *pdev; struct regmap_field *fields[F_MAX_FIELDS]; + + struct cal_dev *cal; + unsigned int instance; }; struct cal_dev { @@ -466,8 +469,7 @@ static u32 cal_data_get_num_csi2_phy(struct cal_dev *cal) } static int cal_camerarx_regmap_init(struct cal_dev *cal, - struct cal_camerarx *phy, - unsigned int idx) + struct cal_camerarx *phy) { const struct cal_camerarx_data *phy_data; unsigned int i; @@ -475,7 +477,7 @@ static int cal_camerarx_regmap_init(struct cal_dev *cal, if (!cal->data) return -EINVAL; - phy_data = &cal->data->camerarx[idx]; + phy_data = &cal->data->camerarx[phy->instance]; for (i = 0; i < F_MAX_FIELDS; i++) { struct reg_field field = { @@ -561,7 +563,8 @@ static void camerarx_phy_disable(struct cal_ctx *ctx) /* * Camera Instance access block */ -static struct cal_camerarx *cc_create(struct cal_dev *cal, unsigned int core) +static struct cal_camerarx *cc_create(struct cal_dev *cal, + unsigned int instance) { struct platform_device *pdev = cal->pdev; struct cal_camerarx *phy; @@ -571,8 +574,11 @@ static struct cal_camerarx *cc_create(struct cal_dev *cal, unsigned int core) if (!phy) return ERR_PTR(-ENOMEM); + phy->cal = cal; + phy->instance = instance; + phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - (core == 0) ? + (instance == 0) ? "cal_rx_core0" : "cal_rx_core1"); phy->base = devm_ioremap_resource(&pdev->dev, phy->res); @@ -584,7 +590,7 @@ static struct cal_camerarx *cc_create(struct cal_dev *cal, unsigned int core) cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", phy->res->name, &phy->res->start, &phy->res->end); - ret = cal_camerarx_regmap_init(cal, phy, core); + ret = cal_camerarx_regmap_init(cal, phy); if (ret) return ERR_PTR(ret); -- cgit v1.2.3 From 552b07b56eba3e7883a69aa0aad80f2c09d87cc1 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:51 +0200 Subject: media: ti-vpe: cal: Use dev_* print macros Use the dev_* print macros instead of the v4l2_* print macros. This prepares for a common print infrastructure that will also support the cal_camerarx instances. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index ff9bc3ae58ba..4b718b4a79c9 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -53,19 +53,23 @@ static unsigned debug; module_param(debug, uint, 0644); MODULE_PARM_DESC(debug, "activates debug info"); -#define cal_dbg(level, cal, fmt, arg...) \ - v4l2_dbg(level, debug, &cal->v4l2_dev, fmt, ##arg) +#define cal_dbg(level, cal, fmt, arg...) \ + do { \ + if (debug >= (level)) \ + dev_printk(KERN_DEBUG, &(cal)->pdev->dev, \ + fmt, ##arg); \ + } while (0) #define cal_info(cal, fmt, arg...) \ - v4l2_info(&cal->v4l2_dev, fmt, ##arg) + dev_info(&(cal)->pdev->dev, fmt, ##arg) #define cal_err(cal, fmt, arg...) \ - v4l2_err(&cal->v4l2_dev, fmt, ##arg) + dev_err(&(cal)->pdev->dev, fmt, ##arg) #define ctx_dbg(level, ctx, fmt, arg...) \ - v4l2_dbg(level, debug, &ctx->v4l2_dev, fmt, ##arg) + cal_dbg(level, (ctx)->cal, "ctx%u: " fmt, (ctx)->csi2_port, ##arg) #define ctx_info(ctx, fmt, arg...) \ - v4l2_info(&ctx->v4l2_dev, fmt, ##arg) + cal_info((ctx)->cal, "ctx%u: " fmt, (ctx)->csi2_port, ##arg) #define ctx_err(ctx, fmt, arg...) \ - v4l2_err(&ctx->v4l2_dev, fmt, ##arg) + cal_err((ctx)->cal, "ctx%u: " fmt, (ctx)->csi2_port, ##arg) #define CAL_NUM_CONTEXT 2 -- cgit v1.2.3 From 6961f5d60108bb8f4a3154bb3780011a26323537 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:52 +0200 Subject: media: ti-vpe: cal: Add print macros for the cal_camerarx instances Prepare for passing the cal_camerarx pointer instead of the cal_ctx pointer to CAMERARX-related functions by adding print macros for cal_camerarx. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 4b718b4a79c9..7ec2b6febf71 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -71,6 +71,13 @@ MODULE_PARM_DESC(debug, "activates debug info"); #define ctx_err(ctx, fmt, arg...) \ cal_err((ctx)->cal, "ctx%u: " fmt, (ctx)->csi2_port, ##arg) +#define phy_dbg(level, phy, fmt, arg...) \ + cal_dbg(level, (phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) +#define phy_info(phy, fmt, arg...) \ + cal_info((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) +#define phy_err(phy, fmt, arg...) \ + cal_err((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) + #define CAL_NUM_CONTEXT 2 #define reg_read(dev, offset) ioread32(dev->base + offset) -- cgit v1.2.3 From 559cd8b836a15f27b40e877e7158373ecb43fc76 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:53 +0200 Subject: media: ti-vpe: cal: Store sensor-related data in cal_camerarx The sensor is connected to the CAMERARX. Store its pointer, as well as the sensor pixel rate and the endpoint, in the cal_camerarx. This prepares for decoupling the cal_camerarx and cal_ctx. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 59 +++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 29 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 7ec2b6febf71..eb5fd81eda37 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -339,6 +339,10 @@ struct cal_camerarx { struct cal_dev *cal; unsigned int instance; + + struct v4l2_fwnode_endpoint endpoint; + struct v4l2_subdev *sensor; + unsigned int external_rate; }; struct cal_dev { @@ -369,8 +373,6 @@ struct cal_ctx { struct v4l2_ctrl_handler ctrl_handler; struct video_device vdev; struct v4l2_async_notifier notifier; - struct v4l2_subdev *sensor; - struct v4l2_fwnode_endpoint endpoint; struct cal_dev *cal; struct cal_camerarx *phy; @@ -394,7 +396,6 @@ struct cal_ctx { unsigned int num_active_fmt; unsigned int sequence; - unsigned int external_rate; struct vb2_queue vb_vidq; unsigned int csi2_port; unsigned int cport; @@ -587,6 +588,7 @@ static struct cal_camerarx *cc_create(struct cal_dev *cal, phy->cal = cal; phy->instance = instance; + phy->external_rate = 192000000; phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, (instance == 0) ? @@ -778,12 +780,12 @@ static void csi2_phy_config(struct cal_ctx *ctx) unsigned int ths_term, ths_settle; unsigned int csi2_ddrclk_khz; struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &ctx->endpoint.bus.mipi_csi2; + &ctx->phy->endpoint.bus.mipi_csi2; u32 num_lanes = mipi_csi2->num_data_lanes; /* DPHY timing configuration */ /* CSI-2 is DDR and we only count used lanes. */ - csi2_ddrclk_khz = ctx->external_rate / 1000 + csi2_ddrclk_khz = ctx->phy->external_rate / 1000 / (2 * num_lanes) * ctx->fmt->bpp; ctx_dbg(1, ctx, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); @@ -978,7 +980,7 @@ static void csi2_lane_config(struct cal_ctx *ctx) u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &ctx->endpoint.bus.mipi_csi2; + &ctx->phy->endpoint.bus.mipi_csi2; int lane; set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); @@ -1149,22 +1151,22 @@ static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) reg_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr); } -static int cal_get_external_info(struct cal_ctx *ctx) +static int cal_get_external_info(struct cal_camerarx *phy) { struct v4l2_ctrl *ctrl; - if (!ctx->sensor) + if (!phy->sensor) return -ENODEV; - ctrl = v4l2_ctrl_find(ctx->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); + ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); if (!ctrl) { - ctx_err(ctx, "no pixel rate control in subdev: %s\n", - ctx->sensor->name); + phy_err(phy, "no pixel rate control in subdev: %s\n", + phy->sensor->name); return -EPIPE; } - ctx->external_rate = v4l2_ctrl_g_ctrl_int64(ctrl); - ctx_dbg(3, ctx, "sensor Pixel Rate: %d\n", ctx->external_rate); + phy->external_rate = v4l2_ctrl_g_ctrl_int64(ctrl); + phy_dbg(3, phy, "sensor Pixel Rate: %u\n", phy->external_rate); return 0; } @@ -1319,7 +1321,7 @@ static int __subdev_get_format(struct cal_ctx *ctx, sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; sd_fmt.pad = 0; - ret = v4l2_subdev_call(ctx->sensor, pad, get_fmt, NULL, &sd_fmt); + ret = v4l2_subdev_call(ctx->phy->sensor, pad, get_fmt, NULL, &sd_fmt); if (ret) return ret; @@ -1342,7 +1344,7 @@ static int __subdev_set_format(struct cal_ctx *ctx, sd_fmt.pad = 0; *mbus_fmt = *fmt; - ret = v4l2_subdev_call(ctx->sensor, pad, set_fmt, NULL, &sd_fmt); + ret = v4l2_subdev_call(ctx->phy->sensor, pad, set_fmt, NULL, &sd_fmt); if (ret) return ret; @@ -1423,7 +1425,7 @@ static int cal_try_fmt_vid_cap(struct file *file, void *priv, fse.code = fmt->code; fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; for (fse.index = 0; ; fse.index++) { - ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size, + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, NULL, &fse); if (ret) break; @@ -1521,7 +1523,8 @@ static int cal_enum_framesizes(struct file *file, void *fh, fse.code = fmt->code; fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size, NULL, &fse); + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, NULL, + &fse); if (ret) return ret; @@ -1577,7 +1580,7 @@ static int cal_enum_frameintervals(struct file *file, void *priv, return -EINVAL; fie.code = fmt->code; - ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_interval, + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_interval, NULL, &fie); if (ret) return ret; @@ -1675,11 +1678,11 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0); ctx->sequence = 0; - ret = cal_get_external_info(ctx); + ret = cal_get_external_info(ctx->phy); if (ret < 0) goto err; - ret = v4l2_subdev_call(ctx->sensor, core, s_power, 1); + ret = v4l2_subdev_call(ctx->phy->sensor, core, s_power, 1); if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { ctx_err(ctx, "power on failed in subdev\n"); goto err; @@ -1696,9 +1699,9 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) enable_irqs(ctx); csi2_phy_init(ctx); - ret = v4l2_subdev_call(ctx->sensor, video, s_stream, 1); + ret = v4l2_subdev_call(ctx->phy->sensor, video, s_stream, 1); if (ret) { - v4l2_subdev_call(ctx->sensor, core, s_power, 0); + v4l2_subdev_call(ctx->phy->sensor, core, s_power, 0); ctx_err(ctx, "stream on failed in subdev\n"); pm_runtime_put_sync(&ctx->cal->pdev->dev); goto err; @@ -1755,10 +1758,10 @@ static void cal_stop_streaming(struct vb2_queue *vq) disable_irqs(ctx); csi2_phy_deinit(ctx); - if (v4l2_subdev_call(ctx->sensor, video, s_stream, 0)) + if (v4l2_subdev_call(ctx->phy->sensor, video, s_stream, 0)) ctx_err(ctx, "stream off failed in subdev\n"); - ret = v4l2_subdev_call(ctx->sensor, core, s_power, 0); + ret = v4l2_subdev_call(ctx->phy->sensor, core, s_power, 0); if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) ctx_err(ctx, "power off failed in subdev\n"); @@ -1849,8 +1852,6 @@ static int cal_complete_ctx(struct cal_ctx *ctx) struct vb2_queue *q; int ret; - ctx->external_rate = 192000000; - /* initialize locks */ spin_lock_init(&ctx->slock); mutex_init(&ctx->mutex); @@ -1906,13 +1907,13 @@ static int cal_async_bound(struct v4l2_async_notifier *notifier, unsigned int i, j, k; int ret = 0; - if (ctx->sensor) { + if (ctx->phy->sensor) { ctx_info(ctx, "Rejecting subdev %s (Already set!!)", subdev->name); return 0; } - ctx->sensor = subdev; + ctx->phy->sensor = subdev; ctx_dbg(1, ctx, "Using sensor %s for capture\n", subdev->name); /* Enumerate sub device formats and enable all matching local formats */ @@ -2065,7 +2066,7 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst) parent = pdev->dev.of_node; - endpoint = &ctx->endpoint; + endpoint = &ctx->phy->endpoint; ep_node = NULL; port = NULL; -- cgit v1.2.3 From f7cd15eb0b852ccc82d6b03e9cde7df9967c5195 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:54 +0200 Subject: media: ti-vpe: cal: Create consistent naming for CAMERARX functions Rename all functions related to CAMERARX with a cal_camerarx_ prefix, and pass them a cal_camerarx pointer. This performs most of the decoupling of the CAMERARX from the context. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 243 ++++++++++++++++++------------------ 1 file changed, 119 insertions(+), 124 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index eb5fd81eda37..bbf7acab17af 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -468,11 +468,9 @@ static inline void set_field(u32 *valp, u32 field, u32 mask) *valp = val; } -static u32 cal_data_get_phy_max_lanes(struct cal_ctx *ctx) +static u32 cal_camerarx_max_lanes(struct cal_camerarx *phy) { - u32 phy_id = ctx->csi2_port; - - return ctx->cal->data->camerarx[phy_id].num_lanes; + return phy->cal->data->camerarx[phy->instance].num_lanes; } static u32 cal_data_get_num_csi2_phy(struct cal_dev *cal) @@ -548,15 +546,13 @@ static struct regmap *cal_get_camerarx_regmap(struct cal_dev *cal) /* * Control Module CAMERARX block access */ -static void camerarx_phy_enable(struct cal_ctx *ctx) +static void cal_camerarx_enable(struct cal_camerarx *phy) { - u32 phy_id = ctx->csi2_port; - struct cal_camerarx *phy = ctx->cal->phy[phy_id]; u32 max_lanes; regmap_field_write(phy->fields[F_CAMMODE], 0); /* Always enable all lanes at the phy control level */ - max_lanes = (1 << cal_data_get_phy_max_lanes(ctx)) - 1; + max_lanes = (1 << cal_camerarx_max_lanes(phy)) - 1; regmap_field_write(phy->fields[F_LANEENABLE], max_lanes); /* F_CSI_MODE is not present on every architecture */ if (phy->fields[F_CSI_MODE]) @@ -564,19 +560,16 @@ static void camerarx_phy_enable(struct cal_ctx *ctx) regmap_field_write(phy->fields[F_CTRLCLKEN], 1); } -static void camerarx_phy_disable(struct cal_ctx *ctx) +static void cal_camerarx_disable(struct cal_camerarx *phy) { - u32 phy_id = ctx->csi2_port; - struct cal_camerarx *phy = ctx->cal->phy[phy_id]; - regmap_field_write(phy->fields[F_CTRLCLKEN], 0); } /* * Camera Instance access block */ -static struct cal_camerarx *cc_create(struct cal_dev *cal, - unsigned int instance) +static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, + unsigned int instance) { struct platform_device *pdev = cal->pdev; struct cal_camerarx *phy; @@ -646,14 +639,14 @@ static void cal_get_hwinfo(struct cal_dev *cal) * Core 0: 0x4845 B828 = 0x0000 0040 * Core 1: 0x4845 B928 = 0x0000 0040 */ -static void i913_errata(struct cal_dev *cal, unsigned int port) +static void cal_camerarx_i913_errata(struct cal_camerarx *phy) { - u32 reg10 = reg_read(cal->phy[port], CAL_CSI2_PHY_REG10); + u32 reg10 = reg_read(phy, CAL_CSI2_PHY_REG10); set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); - cal_dbg(1, cal, "CSI2_%d_REG10 = 0x%08x\n", port, reg10); - reg_write(cal->phy[port], CAL_CSI2_PHY_REG10, reg10); + phy_dbg(1, phy, "CSI2_%d_REG10 = 0x%08x\n", phy->instance, reg10); + reg_write(phy, CAL_CSI2_PHY_REG10, reg10); } static void cal_quickdump_regs(struct cal_dev *cal) @@ -685,7 +678,7 @@ static void cal_quickdump_regs(struct cal_dev *cal) /* * Enable the expected IRQ sources */ -static void enable_irqs(struct cal_ctx *ctx) +static void cal_camerarx_enable_irqs(struct cal_camerarx *phy) { u32 val; @@ -696,49 +689,49 @@ static void enable_irqs(struct cal_ctx *ctx) CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; /* Enable CIO error irqs */ - reg_write(ctx->cal, CAL_HL_IRQENABLE_SET(0), - CAL_HL_IRQ_CIO_MASK(ctx->csi2_port)); - reg_write(ctx->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), + reg_write(phy->cal, CAL_HL_IRQENABLE_SET(0), + CAL_HL_IRQ_CIO_MASK(phy->instance)); + reg_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), cio_err_mask); /* Always enable OCPO error */ - reg_write(ctx->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); + reg_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); /* Enable IRQ_WDMA_END 0/1 */ val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->cal, CAL_HL_IRQENABLE_SET(1), val); + set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + reg_write(phy->cal, CAL_HL_IRQENABLE_SET(1), val); /* Enable IRQ_WDMA_START 0/1 */ val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->cal, CAL_HL_IRQENABLE_SET(2), val); + set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + reg_write(phy->cal, CAL_HL_IRQENABLE_SET(2), val); /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(ctx->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); + reg_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); } -static void disable_irqs(struct cal_ctx *ctx) +static void cal_camerarx_disable_irqs(struct cal_camerarx *phy) { u32 val; /* Disable CIO error irqs */ - reg_write(ctx->cal, CAL_HL_IRQENABLE_CLR(0), - CAL_HL_IRQ_CIO_MASK(ctx->csi2_port)); - reg_write(ctx->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), + reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), + CAL_HL_IRQ_CIO_MASK(phy->instance)); + reg_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0); /* Disable IRQ_WDMA_END 0/1 */ val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->cal, CAL_HL_IRQENABLE_CLR(1), val); + set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(1), val); /* Disable IRQ_WDMA_START 0/1 */ val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->cal, CAL_HL_IRQENABLE_CLR(2), val); + set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(2), val); /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(ctx->cal, CAL_CSI2_VC_IRQENABLE(0), 0); + reg_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0); } -static void csi2_cio_power(struct cal_ctx *ctx, bool enable) +static void cal_camerarx_power(struct cal_camerarx *phy, bool enable) { u32 target_state; unsigned int i; @@ -746,14 +739,14 @@ static void csi2_cio_power(struct cal_ctx *ctx, bool enable) target_state = enable ? CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON : CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF; - reg_write_field(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + reg_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), target_state, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK); for (i = 0; i < 10; i++) { u32 current_state; - current_state = reg_read_field(ctx->cal, - CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + current_state = reg_read_field(phy->cal, + CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK); if (current_state == target_state) @@ -763,7 +756,7 @@ static void csi2_cio_power(struct cal_ctx *ctx, bool enable) } if (i == 10) - ctx_err(ctx, "Failed to power %s complexio\n", + phy_err(phy, "Failed to power %s complexio\n", enable ? "up" : "down"); } @@ -774,49 +767,51 @@ static void csi2_cio_power(struct cal_ctx *ctx, bool enable) #define TCLK_MISS 1 #define TCLK_SETTLE 14 -static void csi2_phy_config(struct cal_ctx *ctx) +static void cal_camerarx_config(struct cal_camerarx *phy, + const struct cal_fmt *fmt) { unsigned int reg0, reg1; unsigned int ths_term, ths_settle; unsigned int csi2_ddrclk_khz; struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &ctx->phy->endpoint.bus.mipi_csi2; + &phy->endpoint.bus.mipi_csi2; u32 num_lanes = mipi_csi2->num_data_lanes; /* DPHY timing configuration */ /* CSI-2 is DDR and we only count used lanes. */ - csi2_ddrclk_khz = ctx->phy->external_rate / 1000 - / (2 * num_lanes) * ctx->fmt->bpp; - ctx_dbg(1, ctx, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); + csi2_ddrclk_khz = phy->external_rate / 1000 + / (2 * num_lanes) * fmt->bpp; + phy_dbg(1, phy, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ ths_term = 20 * csi2_ddrclk_khz / 1000000; - ctx_dbg(1, ctx, "ths_term: %d (0x%02x)\n", ths_term, ths_term); + phy_dbg(1, phy, "ths_term: %d (0x%02x)\n", ths_term, ths_term); /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; - ctx_dbg(1, ctx, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); + phy_dbg(1, phy, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); - reg0 = reg_read(ctx->phy, CAL_CSI2_PHY_REG0); + reg0 = reg_read(phy, CAL_CSI2_PHY_REG0); set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); - ctx_dbg(1, ctx, "CSI2_%d_REG0 = 0x%08x\n", ctx->csi2_port, reg0); - reg_write(ctx->phy, CAL_CSI2_PHY_REG0, reg0); + phy_dbg(1, phy, "CSI2_%d_REG0 = 0x%08x\n", phy->instance, reg0); + reg_write(phy, CAL_CSI2_PHY_REG0, reg0); - reg1 = reg_read(ctx->phy, CAL_CSI2_PHY_REG1); + reg1 = reg_read(phy, CAL_CSI2_PHY_REG1); set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); set_field(®1, TCLK_MISS, CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); - ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x\n", ctx->csi2_port, reg1); - reg_write(ctx->phy, CAL_CSI2_PHY_REG1, reg1); + phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x\n", phy->instance, reg1); + reg_write(phy, CAL_CSI2_PHY_REG1, reg1); } -static void csi2_phy_init(struct cal_ctx *ctx) +static void cal_camerarx_init(struct cal_camerarx *phy, + const struct cal_fmt *fmt) { u32 val; u32 sscounter; @@ -839,21 +834,21 @@ static void csi2_phy_init(struct cal_ctx *ctx) */ /* 1. Configure D-PHY mode and enable required lanes */ - camerarx_phy_enable(ctx); + cal_camerarx_enable(phy); /* 2. Reset complex IO - Do not wait for reset completion */ - reg_write_field(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + reg_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); - ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n", - ctx->csi2_port, - reg_read(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port))); + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n", + phy->instance, + reg_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance))); /* Dummy read to allow SCP reset to complete */ - reg_read(ctx->phy, CAL_CSI2_PHY_REG0); + reg_read(phy, CAL_CSI2_PHY_REG0); /* 3.A. Program Phy Timing Parameters */ - csi2_phy_config(ctx); + cal_camerarx_config(phy, fmt); /* 3.B. Program Stop States */ /* @@ -864,67 +859,67 @@ static void csi2_phy_init(struct cal_ctx *ctx) * Stop-state-timeout must be more than 100us as per CSI2 spec, so we * calculate a timeout that's 100us (rounding up). */ - sscounter = DIV_ROUND_UP(clk_get_rate(ctx->cal->fclk), 10000 * 16 * 4); + sscounter = DIV_ROUND_UP(clk_get_rate(phy->cal->fclk), 10000 * 16 * 4); - val = reg_read(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port)); + val = reg_read(phy->cal, CAL_CSI2_TIMING(phy->instance)); set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK); set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK); set_field(&val, sscounter, CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK); - reg_write(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n", - ctx->csi2_port, - reg_read(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port))); + reg_write(phy->cal, CAL_CSI2_TIMING(phy->instance), val); + phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n", + phy->instance, + reg_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); /* 4. Force FORCERXMODE */ - reg_write_field(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port), + reg_write_field(phy->cal, CAL_CSI2_TIMING(phy->instance), 1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK); - ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n", - ctx->csi2_port, - reg_read(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port))); + phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n", + phy->instance, + reg_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); /* E. Power up the PHY using the complex IO */ - csi2_cio_power(ctx, true); + cal_camerarx_power(phy, true); } -static void csi2_wait_complexio_reset(struct cal_ctx *ctx) +static void cal_camerarx_wait_reset(struct cal_camerarx *phy) { unsigned long timeout; timeout = jiffies + msecs_to_jiffies(750); while (time_before(jiffies, timeout)) { - if (reg_read_field(ctx->cal, - CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + if (reg_read_field(phy->cal, + CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) break; usleep_range(500, 5000); } - if (reg_read_field(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + if (reg_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) != CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) - ctx_err(ctx, "Timeout waiting for Complex IO reset done\n"); + phy_err(phy, "Timeout waiting for Complex IO reset done\n"); } -static void csi2_wait_stop_state(struct cal_ctx *ctx) +static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) { unsigned long timeout; timeout = jiffies + msecs_to_jiffies(750); while (time_before(jiffies, timeout)) { - if (reg_read_field(ctx->cal, - CAL_CSI2_TIMING(ctx->csi2_port), + if (reg_read_field(phy->cal, + CAL_CSI2_TIMING(phy->instance), CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0) break; usleep_range(500, 5000); } - if (reg_read_field(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port), + if (reg_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance), CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0) - ctx_err(ctx, "Timeout waiting for stop state\n"); + phy_err(phy, "Timeout waiting for stop state\n"); } -static void csi2_wait_for_phy(struct cal_ctx *ctx) +static void cal_camerarx_wait_ready(struct cal_camerarx *phy) { /* Steps * 2. Wait for completion of reset @@ -936,51 +931,51 @@ static void csi2_wait_for_phy(struct cal_ctx *ctx) */ /* 2. Wait for reset completion */ - csi2_wait_complexio_reset(ctx); + cal_camerarx_wait_reset(phy); /* 4. G. Wait for all enabled lane to reach stop state */ - csi2_wait_stop_state(ctx); + cal_camerarx_wait_stop_state(phy); - ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x (Bit(31,28) should be set!)\n", - ctx->csi2_port, reg_read(ctx->phy, CAL_CSI2_PHY_REG1)); + phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x (Bit(31,28) should be set!)\n", + phy->instance, reg_read(phy, CAL_CSI2_PHY_REG1)); } -static void csi2_phy_deinit(struct cal_ctx *ctx) +static void cal_camerarx_deinit(struct cal_camerarx *phy) { unsigned int i; - csi2_cio_power(ctx, false); + cal_camerarx_power(phy, false); /* Assert Comple IO Reset */ - reg_write_field(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + reg_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); /* Wait for power down completion */ for (i = 0; i < 10; i++) { - if (reg_read_field(ctx->cal, - CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + if (reg_read_field(phy->cal, + CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING) break; usleep_range(1000, 1100); } - ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n", - ctx->csi2_port, - reg_read(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)), i, + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n", + phy->instance, + reg_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)), i, (i >= 10) ? "(timeout)" : ""); /* Disable the phy */ - camerarx_phy_disable(ctx); + cal_camerarx_disable(phy); } -static void csi2_lane_config(struct cal_ctx *ctx) +static void cal_camerarx_lane_config(struct cal_camerarx *phy) { - u32 val = reg_read(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)); + u32 val = reg_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)); u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &ctx->phy->endpoint.bus.mipi_csi2; + &phy->endpoint.bus.mipi_csi2; int lane; set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); @@ -997,21 +992,21 @@ static void csi2_lane_config(struct cal_ctx *ctx) polarity_mask); } - reg_write(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", - ctx->csi2_port, val); + reg_write(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), val); + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", + phy->instance, val); } -static void csi2_ppi_enable(struct cal_ctx *ctx) +static void cal_camerarx_ppi_enable(struct cal_camerarx *phy) { - reg_write(ctx->cal, CAL_CSI2_PPI_CTRL(ctx->csi2_port), BIT(3)); - reg_write_field(ctx->cal, CAL_CSI2_PPI_CTRL(ctx->csi2_port), + reg_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), BIT(3)); + reg_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } -static void csi2_ppi_disable(struct cal_ctx *ctx) +static void cal_camerarx_ppi_disable(struct cal_camerarx *phy) { - reg_write_field(ctx->cal, CAL_CSI2_PPI_CTRL(ctx->csi2_port), + reg_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } @@ -1151,7 +1146,7 @@ static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) reg_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr); } -static int cal_get_external_info(struct cal_camerarx *phy) +static int cal_camerarx_get_external_info(struct cal_camerarx *phy) { struct v4l2_ctrl *ctrl; @@ -1678,7 +1673,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0); ctx->sequence = 0; - ret = cal_get_external_info(ctx->phy); + ret = cal_camerarx_get_external_info(ctx->phy); if (ret < 0) goto err; @@ -1694,10 +1689,10 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) pix_proc_config(ctx); cal_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline, ctx->v_fmt.fmt.pix.height); - csi2_lane_config(ctx); + cal_camerarx_lane_config(ctx->phy); - enable_irqs(ctx); - csi2_phy_init(ctx); + cal_camerarx_enable_irqs(ctx->phy); + cal_camerarx_init(ctx->phy, ctx->fmt); ret = v4l2_subdev_call(ctx->phy->sensor, video, s_stream, 1); if (ret) { @@ -1707,9 +1702,9 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) goto err; } - csi2_wait_for_phy(ctx); + cal_camerarx_wait_ready(ctx->phy); cal_wr_dma_addr(ctx, addr); - csi2_ppi_enable(ctx); + cal_camerarx_ppi_enable(ctx->phy); if (debug >= 4) cal_quickdump_regs(ctx->cal); @@ -1739,7 +1734,7 @@ static void cal_stop_streaming(struct vb2_queue *vq) int ret; bool dma_act; - csi2_ppi_disable(ctx); + cal_camerarx_ppi_disable(ctx->phy); /* wait for stream and dma to finish */ dma_act = true; @@ -1755,8 +1750,8 @@ static void cal_stop_streaming(struct vb2_queue *vq) if (dma_act) ctx_err(ctx, "failed to disable dma cleanly\n"); - disable_irqs(ctx); - csi2_phy_deinit(ctx); + cal_camerarx_disable_irqs(ctx->phy); + cal_camerarx_deinit(ctx->phy); if (v4l2_subdev_call(ctx->phy->sensor, video, s_stream, 0)) ctx_err(ctx, "stream off failed in subdev\n"); @@ -2330,12 +2325,12 @@ static int cal_probe(struct platform_device *pdev) platform_set_drvdata(pdev, cal); - cal->phy[0] = cc_create(cal, 0); + cal->phy[0] = cal_camerarx_create(cal, 0); if (IS_ERR(cal->phy[0])) return PTR_ERR(cal->phy[0]); if (cal_data_get_num_csi2_phy(cal) > 1) { - cal->phy[1] = cc_create(cal, 1); + cal->phy[1] = cal_camerarx_create(cal, 1); if (IS_ERR(cal->phy[1])) return PTR_ERR(cal->phy[1]); } else { @@ -2400,7 +2395,7 @@ static int cal_remove(struct platform_device *pdev) if (ctx) { ctx_dbg(1, ctx, "unregistering %s\n", video_device_node_name(&ctx->vdev)); - camerarx_phy_disable(ctx); + cal_camerarx_disable(ctx->phy); v4l2_async_notifier_unregister(&ctx->notifier); v4l2_async_notifier_cleanup(&ctx->notifier); v4l2_ctrl_handler_free(&ctx->ctrl_handler); @@ -2426,8 +2421,8 @@ static int cal_runtime_resume(struct device *dev) * Apply errata on both port everytime we (re-)enable * the clock */ - i913_errata(cal, 0); - i913_errata(cal, 1); + cal_camerarx_i913_errata(cal->phy[0]); + cal_camerarx_i913_errata(cal->phy[1]); } return 0; -- cgit v1.2.3 From 825800dc2250ce278fafc24c0cfd4d76d1b51cc3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:55 +0200 Subject: media: ti-vpe: cal: Group CAMERARX-related functions together Group the CAMERARX functions together to make the overall driver structure easier to navigate. This only moves functions around, no functional change is included. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 333 ++++++++++++++++++------------------ 1 file changed, 166 insertions(+), 167 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index bbf7acab17af..57d2feabc2e8 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -468,9 +468,30 @@ static inline void set_field(u32 *valp, u32 field, u32 mask) *valp = val; } -static u32 cal_camerarx_max_lanes(struct cal_camerarx *phy) +static void cal_quickdump_regs(struct cal_dev *cal) { - return phy->cal->data->camerarx[phy->instance].num_lanes; + cal_info(cal, "CAL Registers @ 0x%pa:\n", &cal->res->start); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, + (__force const void *)cal->base, + resource_size(cal->res), false); + + if (cal->ctx[0]) { + cal_info(cal, "CSI2 Core 0 Registers @ %pa:\n", + &cal->ctx[0]->phy->res->start); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, + (__force const void *)cal->ctx[0]->phy->base, + resource_size(cal->ctx[0]->phy->res), + false); + } + + if (cal->ctx[1]) { + cal_info(cal, "CSI2 Core 1 Registers @ %pa:\n", + &cal->ctx[1]->phy->res->start); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, + (__force const void *)cal->ctx[1]->phy->base, + resource_size(cal->ctx[1]->phy->res), + false); + } } static u32 cal_data_get_num_csi2_phy(struct cal_dev *cal) @@ -478,74 +499,16 @@ static u32 cal_data_get_num_csi2_phy(struct cal_dev *cal) return cal->data->num_csi2_phy; } -static int cal_camerarx_regmap_init(struct cal_dev *cal, - struct cal_camerarx *phy) -{ - const struct cal_camerarx_data *phy_data; - unsigned int i; - - if (!cal->data) - return -EINVAL; - - phy_data = &cal->data->camerarx[phy->instance]; - - for (i = 0; i < F_MAX_FIELDS; i++) { - struct reg_field field = { - .reg = cal->syscon_camerrx_offset, - .lsb = phy_data->fields[i].lsb, - .msb = phy_data->fields[i].msb, - }; - - /* - * Here we update the reg offset with the - * value found in DT - */ - phy->fields[i] = devm_regmap_field_alloc(&cal->pdev->dev, - cal->syscon_camerrx, - field); - if (IS_ERR(phy->fields[i])) { - cal_err(cal, "Unable to allocate regmap fields\n"); - return PTR_ERR(phy->fields[i]); - } - } - - return 0; -} +/* ------------------------------------------------------------------ + * CAMERARX Management + * ------------------------------------------------------------------ + */ -static struct regmap *cal_get_camerarx_regmap(struct cal_dev *cal) +static u32 cal_camerarx_max_lanes(struct cal_camerarx *phy) { - struct platform_device *pdev = cal->pdev; - struct regmap_config config = { }; - struct regmap *regmap; - void __iomem *base; - struct resource *res; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "camerrx_control"); - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) { - cal_err(cal, "failed to ioremap\n"); - return ERR_CAST(base); - } - - cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", - res->name, &res->start, &res->end); - - config.reg_bits = 32; - config.reg_stride = 4; - config.val_bits = 32; - config.max_register = resource_size(res) - 4; - - regmap = regmap_init_mmio(NULL, base, &config); - if (IS_ERR(regmap)) - pr_err("regmap init failed\n"); - - return regmap; + return phy->cal->data->camerarx[phy->instance].num_lanes; } -/* - * Control Module CAMERARX block access - */ static void cal_camerarx_enable(struct cal_camerarx *phy) { u32 max_lanes; @@ -565,61 +528,6 @@ static void cal_camerarx_disable(struct cal_camerarx *phy) regmap_field_write(phy->fields[F_CTRLCLKEN], 0); } -/* - * Camera Instance access block - */ -static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, - unsigned int instance) -{ - struct platform_device *pdev = cal->pdev; - struct cal_camerarx *phy; - int ret; - - phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); - if (!phy) - return ERR_PTR(-ENOMEM); - - phy->cal = cal; - phy->instance = instance; - phy->external_rate = 192000000; - - phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - (instance == 0) ? - "cal_rx_core0" : - "cal_rx_core1"); - phy->base = devm_ioremap_resource(&pdev->dev, phy->res); - if (IS_ERR(phy->base)) { - cal_err(cal, "failed to ioremap\n"); - return ERR_CAST(phy->base); - } - - cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", - phy->res->name, &phy->res->start, &phy->res->end); - - ret = cal_camerarx_regmap_init(cal, phy); - if (ret) - return ERR_PTR(ret); - - return phy; -} - -/* - * Get Revision and HW info - */ -static void cal_get_hwinfo(struct cal_dev *cal) -{ - u32 revision; - u32 hwinfo; - - revision = reg_read(cal, CAL_HL_REVISION); - cal_dbg(3, cal, "CAL_HL_REVISION = 0x%08x (expecting 0x40000200)\n", - revision); - - hwinfo = reg_read(cal, CAL_HL_HWINFO); - cal_dbg(3, cal, "CAL_HL_HWINFO = 0x%08x (expecting 0xA3C90469)\n", - hwinfo); -} - /* * Errata i913: CSI2 LDO Needs to be disabled when module is powered on * @@ -649,32 +557,6 @@ static void cal_camerarx_i913_errata(struct cal_camerarx *phy) reg_write(phy, CAL_CSI2_PHY_REG10, reg10); } -static void cal_quickdump_regs(struct cal_dev *cal) -{ - cal_info(cal, "CAL Registers @ 0x%pa:\n", &cal->res->start); - print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)cal->base, - resource_size(cal->res), false); - - if (cal->ctx[0]) { - cal_info(cal, "CSI2 Core 0 Registers @ %pa:\n", - &cal->ctx[0]->phy->res->start); - print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)cal->ctx[0]->phy->base, - resource_size(cal->ctx[0]->phy->res), - false); - } - - if (cal->ctx[1]) { - cal_info(cal, "CSI2 Core 1 Registers @ %pa:\n", - &cal->ctx[1]->phy->res->start); - print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)cal->ctx[1]->phy->base, - resource_size(cal->ctx[1]->phy->res), - false); - } -} - /* * Enable the expected IRQ sources */ @@ -1010,6 +892,126 @@ static void cal_camerarx_ppi_disable(struct cal_camerarx *phy) 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } +static int cal_camerarx_get_external_info(struct cal_camerarx *phy) +{ + struct v4l2_ctrl *ctrl; + + if (!phy->sensor) + return -ENODEV; + + ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); + if (!ctrl) { + phy_err(phy, "no pixel rate control in subdev: %s\n", + phy->sensor->name); + return -EPIPE; + } + + phy->external_rate = v4l2_ctrl_g_ctrl_int64(ctrl); + phy_dbg(3, phy, "sensor Pixel Rate: %u\n", phy->external_rate); + + return 0; +} + +static int cal_camerarx_regmap_init(struct cal_dev *cal, + struct cal_camerarx *phy) +{ + const struct cal_camerarx_data *phy_data; + unsigned int i; + + if (!cal->data) + return -EINVAL; + + phy_data = &cal->data->camerarx[phy->instance]; + + for (i = 0; i < F_MAX_FIELDS; i++) { + struct reg_field field = { + .reg = cal->syscon_camerrx_offset, + .lsb = phy_data->fields[i].lsb, + .msb = phy_data->fields[i].msb, + }; + + /* + * Here we update the reg offset with the + * value found in DT + */ + phy->fields[i] = devm_regmap_field_alloc(&cal->pdev->dev, + cal->syscon_camerrx, + field); + if (IS_ERR(phy->fields[i])) { + cal_err(cal, "Unable to allocate regmap fields\n"); + return PTR_ERR(phy->fields[i]); + } + } + + return 0; +} + +static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, + unsigned int instance) +{ + struct platform_device *pdev = cal->pdev; + struct cal_camerarx *phy; + int ret; + + phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return ERR_PTR(-ENOMEM); + + phy->cal = cal; + phy->instance = instance; + phy->external_rate = 192000000; + + phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + (instance == 0) ? + "cal_rx_core0" : + "cal_rx_core1"); + phy->base = devm_ioremap_resource(&pdev->dev, phy->res); + if (IS_ERR(phy->base)) { + cal_err(cal, "failed to ioremap\n"); + return ERR_CAST(phy->base); + } + + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", + phy->res->name, &phy->res->start, &phy->res->end); + + ret = cal_camerarx_regmap_init(cal, phy); + if (ret) + return ERR_PTR(ret); + + return phy; +} + +static struct regmap *cal_get_camerarx_regmap(struct cal_dev *cal) +{ + struct platform_device *pdev = cal->pdev; + struct regmap_config config = { }; + struct regmap *regmap; + void __iomem *base; + struct resource *res; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "camerrx_control"); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) { + cal_err(cal, "failed to ioremap\n"); + return ERR_CAST(base); + } + + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", + res->name, &res->start, &res->end); + + config.reg_bits = 32; + config.reg_stride = 4; + config.val_bits = 32; + config.max_register = resource_size(res) - 4; + + regmap = regmap_init_mmio(NULL, base, &config); + if (IS_ERR(regmap)) + pr_err("regmap init failed\n"); + + return regmap; +} + static void csi2_ctx_config(struct cal_ctx *ctx) { u32 val; @@ -1146,26 +1148,6 @@ static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) reg_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr); } -static int cal_camerarx_get_external_info(struct cal_camerarx *phy) -{ - struct v4l2_ctrl *ctrl; - - if (!phy->sensor) - return -ENODEV; - - ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); - if (!ctrl) { - phy_err(phy, "no pixel rate control in subdev: %s\n", - phy->sensor->name); - return -EPIPE; - } - - phy->external_rate = v4l2_ctrl_g_ctrl_int64(ctrl); - phy_dbg(3, phy, "sensor Pixel Rate: %u\n", phy->external_rate); - - return 0; -} - static inline void cal_schedule_next_buffer(struct cal_ctx *ctx) { struct cal_dmaqueue *dma_q = &ctx->vidq; @@ -2244,6 +2226,23 @@ static const struct of_device_id cal_of_match[] = { }; MODULE_DEVICE_TABLE(of, cal_of_match); +/* + * Get Revision and HW info + */ +static void cal_get_hwinfo(struct cal_dev *cal) +{ + u32 revision; + u32 hwinfo; + + revision = reg_read(cal, CAL_HL_REVISION); + cal_dbg(3, cal, "CAL_HL_REVISION = 0x%08x (expecting 0x40000200)\n", + revision); + + hwinfo = reg_read(cal, CAL_HL_HWINFO); + cal_dbg(3, cal, "CAL_HL_HWINFO = 0x%08x (expecting 0xA3C90469)\n", + hwinfo); +} + static int cal_probe(struct platform_device *pdev) { struct cal_dev *cal; -- cgit v1.2.3 From f3ca1f9db5b9f3c0d3b3bf890ae92791852aff37 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:56 +0200 Subject: media: ti-vpe: cal: Inline cal_data_get_num_csi2_phy() in its caller The cal_data_get_num_csi2_phy() function simply returns cal->data->num_csi2_phy, and is not consistently used across the driver, as most accesses to cal->data->num_csi2_phy are open-coded. We could fix those open-coded accesses to use cal_data_get_num_csi2_phy(), but that wouldn't bring much in terms of readability, so inline cal_data_get_num_csi2_phy() in its only caller instead, and drop the function. Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 57d2feabc2e8..5b89d03d1f62 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -494,11 +494,6 @@ static void cal_quickdump_regs(struct cal_dev *cal) } } -static u32 cal_data_get_num_csi2_phy(struct cal_dev *cal) -{ - return cal->data->num_csi2_phy; -} - /* ------------------------------------------------------------------ * CAMERARX Management * ------------------------------------------------------------------ @@ -2328,7 +2323,7 @@ static int cal_probe(struct platform_device *pdev) if (IS_ERR(cal->phy[0])) return PTR_ERR(cal->phy[0]); - if (cal_data_get_num_csi2_phy(cal) > 1) { + if (cal->data->num_csi2_phy > 1) { cal->phy[1] = cal_camerarx_create(cal, 1); if (IS_ERR(cal->phy[1])) return PTR_ERR(cal->phy[1]); @@ -2340,7 +2335,7 @@ static int cal_probe(struct platform_device *pdev) cal->ctx[1] = NULL; cal->ctx[0] = cal_create_instance(cal, 0); - if (cal_data_get_num_csi2_phy(cal) > 1) + if (cal->data->num_csi2_phy > 1) cal->ctx[1] = cal_create_instance(cal, 1); if (!cal->ctx[0] && !cal->ctx[1]) { cal_err(cal, "Neither port is configured, no point in staying up\n"); -- cgit v1.2.3 From 9f2b94b1ca7d6431697ea8f190a6493cf221364b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:57 +0200 Subject: media: ti-vpe: cal: Create consistent naming for context functions Rename all functions related to contexts with a cal_ctx_ prefix to increase readability. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 5b89d03d1f62..7914a0f22f0e 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1007,7 +1007,12 @@ static struct regmap *cal_get_camerarx_regmap(struct cal_dev *cal) return regmap; } -static void csi2_ctx_config(struct cal_ctx *ctx) +/* ------------------------------------------------------------------ + * Context Management + * ------------------------------------------------------------------ + */ + +static void cal_ctx_csi2_config(struct cal_ctx *ctx) { u32 val; @@ -1033,7 +1038,7 @@ static void csi2_ctx_config(struct cal_ctx *ctx) reg_read(ctx->cal, CAL_CSI2_CTX0(ctx->csi2_port))); } -static void pix_proc_config(struct cal_ctx *ctx) +static void cal_ctx_pix_proc_config(struct cal_ctx *ctx) { u32 val, extract, pack; @@ -1084,8 +1089,8 @@ static void pix_proc_config(struct cal_ctx *ctx) reg_read(ctx->cal, CAL_PIX_PROC(ctx->csi2_port))); } -static void cal_wr_dma_config(struct cal_ctx *ctx, - unsigned int width, unsigned int height) +static void cal_ctx_wr_dma_config(struct cal_ctx *ctx, + unsigned int width, unsigned int height) { u32 val; @@ -1138,7 +1143,7 @@ static void cal_wr_dma_config(struct cal_ctx *ctx, ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", reg_read(ctx->cal, CAL_CTRL)); } -static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) +static void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) { reg_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr); } @@ -1154,7 +1159,7 @@ static inline void cal_schedule_next_buffer(struct cal_ctx *ctx) list_del(&buf->list); addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); - cal_wr_dma_addr(ctx, addr); + cal_ctx_wr_dma_addr(ctx, addr); } static inline void cal_process_buffer_complete(struct cal_ctx *ctx) @@ -1662,10 +1667,10 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) pm_runtime_get_sync(&ctx->cal->pdev->dev); - csi2_ctx_config(ctx); - pix_proc_config(ctx); - cal_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline, - ctx->v_fmt.fmt.pix.height); + cal_ctx_csi2_config(ctx); + cal_ctx_pix_proc_config(ctx); + cal_ctx_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline, + ctx->v_fmt.fmt.pix.height); cal_camerarx_lane_config(ctx->phy); cal_camerarx_enable_irqs(ctx->phy); @@ -1680,7 +1685,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) } cal_camerarx_wait_ready(ctx->phy); - cal_wr_dma_addr(ctx, addr); + cal_ctx_wr_dma_addr(ctx, addr); cal_camerarx_ppi_enable(ctx->phy); if (debug >= 4) -- cgit v1.2.3 From 6770318dc07287b3506753927ea1c9a99c20fe1e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:58 +0200 Subject: media: ti-vpe: cal: Reorganize remaining code in sections Increase readability by reorganizing the remaining code in sections. No functional change is included. Signed-off-by: Laurent Pinchart Reviewed-by: Kieran Bingham Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 340 +++++++++++++++++++----------------- 1 file changed, 182 insertions(+), 158 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 7914a0f22f0e..d452c3e950f0 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -37,9 +37,6 @@ #define CAL_MODULE_NAME "cal" -#define MAX_WIDTH_BYTES (8192 * 8) -#define MAX_HEIGHT_LINES 16383 - MODULE_DESCRIPTION("TI CAL driver"); MODULE_AUTHOR("Benoit Parrot, "); MODULE_LICENSE("GPL v2"); @@ -80,11 +77,11 @@ MODULE_PARM_DESC(debug, "activates debug info"); #define CAL_NUM_CONTEXT 2 -#define reg_read(dev, offset) ioread32(dev->base + offset) -#define reg_write(dev, offset, val) iowrite32(val, dev->base + offset) +#define MAX_WIDTH_BYTES (8192 * 8) +#define MAX_HEIGHT_LINES 16383 /* ------------------------------------------------------------------ - * Basic structures + * Format Handling * ------------------------------------------------------------------ */ @@ -205,6 +202,11 @@ static char *fourcc_to_str(u32 fmt) return code; } +/* ------------------------------------------------------------------ + * Driver Structures + * ------------------------------------------------------------------ + */ + /* buffer for one video frame */ struct cal_buffer { /* common v4l buffer stuff -- must be first */ @@ -240,80 +242,6 @@ struct cal_data { unsigned int flags; }; -static const struct cal_camerarx_data dra72x_cal_camerarx[] = { - { - .fields = { - [F_CTRLCLKEN] = { 10, 10 }, - [F_CAMMODE] = { 11, 12 }, - [F_LANEENABLE] = { 13, 16 }, - [F_CSI_MODE] = { 17, 17 }, - }, - .num_lanes = 4, - }, - { - .fields = { - [F_CTRLCLKEN] = { 0, 0 }, - [F_CAMMODE] = { 1, 2 }, - [F_LANEENABLE] = { 3, 4 }, - [F_CSI_MODE] = { 5, 5 }, - }, - .num_lanes = 2, - }, -}; - -static const struct cal_data dra72x_cal_data = { - .camerarx = dra72x_cal_camerarx, - .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), -}; - -static const struct cal_data dra72x_es1_cal_data = { - .camerarx = dra72x_cal_camerarx, - .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), - .flags = DRA72_CAL_PRE_ES2_LDO_DISABLE, -}; - -static const struct cal_camerarx_data dra76x_cal_csi_phy[] = { - { - .fields = { - [F_CTRLCLKEN] = { 8, 8 }, - [F_CAMMODE] = { 9, 10 }, - [F_CSI_MODE] = { 11, 11 }, - [F_LANEENABLE] = { 27, 31 }, - }, - .num_lanes = 5, - }, - { - .fields = { - [F_CTRLCLKEN] = { 0, 0 }, - [F_CAMMODE] = { 1, 2 }, - [F_CSI_MODE] = { 3, 3 }, - [F_LANEENABLE] = { 24, 26 }, - }, - .num_lanes = 3, - }, -}; - -static const struct cal_data dra76x_cal_data = { - .camerarx = dra76x_cal_csi_phy, - .num_csi2_phy = ARRAY_SIZE(dra76x_cal_csi_phy), -}; - -static const struct cal_camerarx_data am654_cal_csi_phy[] = { - { - .fields = { - [F_CTRLCLKEN] = { 15, 15 }, - [F_CAMMODE] = { 24, 25 }, - [F_LANEENABLE] = { 0, 4 }, - }, - .num_lanes = 5, - }, -}; - -static const struct cal_data am654_cal_data = { - .camerarx = am654_cal_csi_phy, - .num_csi2_phy = ARRAY_SIZE(am654_cal_csi_phy), -}; - /* * The Camera Adaptation Layer (CAL) module is paired with one or more complex * I/O PHYs (CAMERARX). It contains multiple instances of CSI-2, processing and @@ -409,54 +337,111 @@ struct cal_ctx { bool dma_act; }; -static inline u32 reg_read_field(struct cal_dev *cal, u32 offset, u32 mask) +static inline struct cal_ctx *notifier_to_ctx(struct v4l2_async_notifier *n) { - return FIELD_GET(mask, reg_read(cal, offset)); + return container_of(n, struct cal_ctx, notifier); } -static inline void reg_write_field(struct cal_dev *cal, u32 offset, u32 value, - u32 mask) -{ - u32 val = reg_read(cal, offset); +/* ------------------------------------------------------------------ + * Platform Data + * ------------------------------------------------------------------ + */ - val &= ~mask; - val |= FIELD_PREP(mask, value); - reg_write(cal, offset, val); -} +static const struct cal_camerarx_data dra72x_cal_camerarx[] = { + { + .fields = { + [F_CTRLCLKEN] = { 10, 10 }, + [F_CAMMODE] = { 11, 12 }, + [F_LANEENABLE] = { 13, 16 }, + [F_CSI_MODE] = { 17, 17 }, + }, + .num_lanes = 4, + }, + { + .fields = { + [F_CTRLCLKEN] = { 0, 0 }, + [F_CAMMODE] = { 1, 2 }, + [F_LANEENABLE] = { 3, 4 }, + [F_CSI_MODE] = { 5, 5 }, + }, + .num_lanes = 2, + }, +}; -static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx, - u32 pixelformat) -{ - const struct cal_fmt *fmt; - unsigned int k; +static const struct cal_data dra72x_cal_data = { + .camerarx = dra72x_cal_camerarx, + .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), +}; - for (k = 0; k < ctx->num_active_fmt; k++) { - fmt = ctx->active_fmt[k]; - if (fmt->fourcc == pixelformat) - return fmt; - } +static const struct cal_data dra72x_es1_cal_data = { + .camerarx = dra72x_cal_camerarx, + .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), + .flags = DRA72_CAL_PRE_ES2_LDO_DISABLE, +}; - return NULL; -} +static const struct cal_camerarx_data dra76x_cal_csi_phy[] = { + { + .fields = { + [F_CTRLCLKEN] = { 8, 8 }, + [F_CAMMODE] = { 9, 10 }, + [F_CSI_MODE] = { 11, 11 }, + [F_LANEENABLE] = { 27, 31 }, + }, + .num_lanes = 5, + }, + { + .fields = { + [F_CTRLCLKEN] = { 0, 0 }, + [F_CAMMODE] = { 1, 2 }, + [F_CSI_MODE] = { 3, 3 }, + [F_LANEENABLE] = { 24, 26 }, + }, + .num_lanes = 3, + }, +}; -static const struct cal_fmt *find_format_by_code(struct cal_ctx *ctx, - u32 code) -{ - const struct cal_fmt *fmt; - unsigned int k; +static const struct cal_data dra76x_cal_data = { + .camerarx = dra76x_cal_csi_phy, + .num_csi2_phy = ARRAY_SIZE(dra76x_cal_csi_phy), +}; - for (k = 0; k < ctx->num_active_fmt; k++) { - fmt = ctx->active_fmt[k]; - if (fmt->code == code) - return fmt; - } +static const struct cal_camerarx_data am654_cal_csi_phy[] = { + { + .fields = { + [F_CTRLCLKEN] = { 15, 15 }, + [F_CAMMODE] = { 24, 25 }, + [F_LANEENABLE] = { 0, 4 }, + }, + .num_lanes = 5, + }, +}; - return NULL; +static const struct cal_data am654_cal_data = { + .camerarx = am654_cal_csi_phy, + .num_csi2_phy = ARRAY_SIZE(am654_cal_csi_phy), +}; + +/* ------------------------------------------------------------------ + * I/O Register Accessors + * ------------------------------------------------------------------ + */ + +#define reg_read(dev, offset) ioread32(dev->base + offset) +#define reg_write(dev, offset, val) iowrite32(val, dev->base + offset) + +static inline u32 reg_read_field(struct cal_dev *cal, u32 offset, u32 mask) +{ + return FIELD_GET(mask, reg_read(cal, offset)); } -static inline struct cal_ctx *notifier_to_ctx(struct v4l2_async_notifier *n) +static inline void reg_write_field(struct cal_dev *cal, u32 offset, u32 value, + u32 mask) { - return container_of(n, struct cal_ctx, notifier); + u32 val = reg_read(cal, offset); + + val &= ~mask; + val |= FIELD_PREP(mask, value); + reg_write(cal, offset, val); } static inline void set_field(u32 *valp, u32 field, u32 mask) @@ -1148,6 +1133,11 @@ static void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) reg_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr); } +/* ------------------------------------------------------------------ + * IRQ Handling + * ------------------------------------------------------------------ + */ + static inline void cal_schedule_next_buffer(struct cal_ctx *ctx) { struct cal_dmaqueue *dma_q = &ctx->vidq; @@ -1256,9 +1246,41 @@ static irqreturn_t cal_irq(int irq_cal, void *data) return IRQ_HANDLED; } -/* - * video ioctls +/* ------------------------------------------------------------------ + * V4L2 Video IOCTLs + * ------------------------------------------------------------------ */ + +static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx, + u32 pixelformat) +{ + const struct cal_fmt *fmt; + unsigned int k; + + for (k = 0; k < ctx->num_active_fmt; k++) { + fmt = ctx->active_fmt[k]; + if (fmt->fourcc == pixelformat) + return fmt; + } + + return NULL; +} + +static const struct cal_fmt *find_format_by_code(struct cal_ctx *ctx, + u32 code) +{ + const struct cal_fmt *fmt; + unsigned int k; + + for (k = 0; k < ctx->num_active_fmt; k++) { + fmt = ctx->active_fmt[k]; + if (fmt->code == code) + return fmt; + } + + return NULL; +} + static int cal_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -1567,9 +1589,46 @@ static int cal_enum_frameintervals(struct file *file, void *priv, return 0; } -/* - * Videobuf operations +static const struct v4l2_file_operations cal_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .mmap = vb2_fop_mmap, +}; + +static const struct v4l2_ioctl_ops cal_ioctl_ops = { + .vidioc_querycap = cal_querycap, + .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap, + .vidioc_enum_framesizes = cal_enum_framesizes, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_enum_input = cal_enum_input, + .vidioc_g_input = cal_g_input, + .vidioc_s_input = cal_s_input, + .vidioc_enum_frameintervals = cal_enum_frameintervals, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* ------------------------------------------------------------------ + * videobuf2 Operations + * ------------------------------------------------------------------ */ + static int cal_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[]) @@ -1773,40 +1832,10 @@ static const struct vb2_ops cal_video_qops = { .wait_finish = vb2_ops_wait_finish, }; -static const struct v4l2_file_operations cal_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .read = vb2_fop_read, - .poll = vb2_fop_poll, - .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ - .mmap = vb2_fop_mmap, -}; - -static const struct v4l2_ioctl_ops cal_ioctl_ops = { - .vidioc_querycap = cal_querycap, - .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap, - .vidioc_enum_framesizes = cal_enum_framesizes, - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_enum_input = cal_enum_input, - .vidioc_g_input = cal_g_input, - .vidioc_s_input = cal_s_input, - .vidioc_enum_frameintervals = cal_enum_frameintervals, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; +/* ------------------------------------------------------------------ + * Initialization and module stuff + * ------------------------------------------------------------------ + */ static const struct video_device cal_videodev = { .name = CAL_MODULE_NAME, @@ -1818,11 +1847,6 @@ static const struct video_device cal_videodev = { V4L2_CAP_READWRITE, }; -/* ----------------------------------------------------------------- - * Initialization and module stuff - * ------------------------------------------------------------------ - */ - static int cal_complete_ctx(struct cal_ctx *ctx) { struct video_device *vfd; -- cgit v1.2.3 From 20d4eb10911acb5a7f65816393532f451f166c2b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:59 +0200 Subject: media: ti-vpe: cal: Rename cal_ctx.csi2_port to cal_ctx.index The csi2_port field of the cal_ctx structure holds the context index, and is independent from the CSI-2 port (even if it currently has the same numerical value). Rename it to index to avoid the ambiguity. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 50 ++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 25 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index d452c3e950f0..ac23c41230fb 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -62,11 +62,11 @@ MODULE_PARM_DESC(debug, "activates debug info"); dev_err(&(cal)->pdev->dev, fmt, ##arg) #define ctx_dbg(level, ctx, fmt, arg...) \ - cal_dbg(level, (ctx)->cal, "ctx%u: " fmt, (ctx)->csi2_port, ##arg) + cal_dbg(level, (ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) #define ctx_info(ctx, fmt, arg...) \ - cal_info((ctx)->cal, "ctx%u: " fmt, (ctx)->csi2_port, ##arg) + cal_info((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) #define ctx_err(ctx, fmt, arg...) \ - cal_err((ctx)->cal, "ctx%u: " fmt, (ctx)->csi2_port, ##arg) + cal_err((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) #define phy_dbg(level, phy, fmt, arg...) \ cal_dbg(level, (phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) @@ -325,7 +325,7 @@ struct cal_ctx { unsigned int sequence; struct vb2_queue vb_vidq; - unsigned int csi2_port; + unsigned int index; unsigned int cport; unsigned int virtual_channel; @@ -1001,7 +1001,7 @@ static void cal_ctx_csi2_config(struct cal_ctx *ctx) { u32 val; - val = reg_read(ctx->cal, CAL_CSI2_CTX0(ctx->csi2_port)); + val = reg_read(ctx->cal, CAL_CSI2_CTX0(ctx->index)); set_field(&val, ctx->cport, CAL_CSI2_CTX_CPORT_MASK); /* * DT type: MIPI CSI-2 Specs @@ -1018,9 +1018,9 @@ static void cal_ctx_csi2_config(struct cal_ctx *ctx) set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK); set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE, CAL_CSI2_CTX_PACK_MODE_MASK); - reg_write(ctx->cal, CAL_CSI2_CTX0(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->cal, CAL_CSI2_CTX0(ctx->csi2_port))); + reg_write(ctx->cal, CAL_CSI2_CTX0(ctx->index), val); + ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->index, + reg_read(ctx->cal, CAL_CSI2_CTX0(ctx->index))); } static void cal_ctx_pix_proc_config(struct cal_ctx *ctx) @@ -1062,16 +1062,16 @@ static void cal_ctx_pix_proc_config(struct cal_ctx *ctx) break; } - val = reg_read(ctx->cal, CAL_PIX_PROC(ctx->csi2_port)); + val = reg_read(ctx->cal, CAL_PIX_PROC(ctx->index)); set_field(&val, extract, CAL_PIX_PROC_EXTRACT_MASK); set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK); set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK); set_field(&val, pack, CAL_PIX_PROC_PACK_MASK); set_field(&val, ctx->cport, CAL_PIX_PROC_CPORT_MASK); set_field(&val, 1, CAL_PIX_PROC_EN_MASK); - reg_write(ctx->cal, CAL_PIX_PROC(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->cal, CAL_PIX_PROC(ctx->csi2_port))); + reg_write(ctx->cal, CAL_PIX_PROC(ctx->index), val); + ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->index, + reg_read(ctx->cal, CAL_PIX_PROC(ctx->index))); } static void cal_ctx_wr_dma_config(struct cal_ctx *ctx, @@ -1079,7 +1079,7 @@ static void cal_ctx_wr_dma_config(struct cal_ctx *ctx, { u32 val; - val = reg_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->csi2_port)); + val = reg_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index)); set_field(&val, ctx->cport, CAL_WR_DMA_CTRL_CPORT_MASK); set_field(&val, height, CAL_WR_DMA_CTRL_YSIZE_MASK); set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT, @@ -1089,22 +1089,22 @@ static void cal_ctx_wr_dma_config(struct cal_ctx *ctx, set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR, CAL_WR_DMA_CTRL_PATTERN_MASK); set_field(&val, 1, CAL_WR_DMA_CTRL_STALL_RD_MASK); - reg_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->csi2_port))); + reg_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->index), val); + ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->index, + reg_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index))); /* * width/16 not sure but giving it a whirl. * zero does not work right */ reg_write_field(ctx->cal, - CAL_WR_DMA_OFST(ctx->csi2_port), + CAL_WR_DMA_OFST(ctx->index), (width / 16), CAL_WR_DMA_OFST_MASK); - ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->cal, CAL_WR_DMA_OFST(ctx->csi2_port))); + ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->index, + reg_read(ctx->cal, CAL_WR_DMA_OFST(ctx->index))); - val = reg_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->csi2_port)); + val = reg_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index)); /* 64 bit word means no skipping */ set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK); /* @@ -1113,9 +1113,9 @@ static void cal_ctx_wr_dma_config(struct cal_ctx *ctx, * is detected automagically */ set_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK); - reg_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->csi2_port))); + reg_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index), val); + ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->index, + reg_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index))); val = reg_read(ctx->cal, CAL_CTRL); set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, CAL_CTRL_BURSTSIZE_MASK); @@ -1130,7 +1130,7 @@ static void cal_ctx_wr_dma_config(struct cal_ctx *ctx, static void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) { - reg_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr); + reg_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), dmaaddr); } /* ------------------------------------------------------------------ @@ -2211,7 +2211,7 @@ static struct cal_ctx *cal_create_instance(struct cal_dev *cal, int inst) ctx->phy = cal->phy[inst]; /* Store the instance id */ - ctx->csi2_port = inst; + ctx->index = inst; ctx->cport = inst; ret = of_cal_create_instance(ctx, inst); -- cgit v1.2.3 From 17513da19e0a170d0a63c6c80e5d88cbf3eb1788 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:00 +0200 Subject: media: ti-vpe: cal: Use correct device name for bus_info The v4l2_capability bus_info field, filled by the VIDIOC_QUERYCAP ioctl handler, specifies the location of the device in the system. For platform devices, V4L2 specifies that the value must be "platform:" followed by the device name. Fix the cal_querycap() function to set the right value. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index ac23c41230fb..028479498c07 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1290,7 +1290,7 @@ static int cal_querycap(struct file *file, void *priv, strscpy(cap->card, CAL_MODULE_NAME, sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), - "platform:%s", ctx->v4l2_dev.name); + "platform:%s", dev_name(&ctx->cal->pdev->dev)); return 0; } -- cgit v1.2.3 From 281736ef5545bb07b3291d00f655d6f884df9f80 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:01 +0200 Subject: media: ti-vpe: cal: Get struct device without going through v4l2_device Instead of retrieving the struct device for the platform device from the v4l2_device, get it from the platform device directly. This prepares for cleanups related to v4l2_device handling. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 028479498c07..22679baec364 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1868,7 +1868,7 @@ static int cal_complete_ctx(struct cal_ctx *ctx) q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &ctx->mutex; q->min_buffers_needed = 3; - q->dev = ctx->v4l2_dev.dev; + q->dev = &ctx->cal->pdev->dev; ret = vb2_queue_init(q); if (ret) -- cgit v1.2.3 From ed595da7d70fbb12bb5bd30936b472164082c484 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:02 +0200 Subject: media: ti-vpe: cal: Use ctx_info() instead of v4l2_info() Use the context-specific print macro to replace the last usage of the v4l2_* print macros. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 22679baec364..b55c8fb369a0 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1893,8 +1893,8 @@ static int cal_complete_ctx(struct cal_ctx *ctx) if (ret < 0) return ret; - v4l2_info(&ctx->v4l2_dev, "V4L2 device registered as %s\n", - video_device_node_name(vfd)); + ctx_info(ctx, "V4L2 device registered as %s\n", + video_device_node_name(vfd)); return 0; } -- cgit v1.2.3 From 2f63379b93fa389ed65432d456da6697f60f7338 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:03 +0200 Subject: media: ti-vpe: cal: Use a loop to create CAMERARX and context instances Replace a manually unrolled loop with an explicit for loop to increase readability when creating the CAMERARX and context instances. The explicit NULL initialization of cal->phy[] and cal->ctx[] is removed, as the cal structure is zeroed when allocated. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index b55c8fb369a0..f92d6e49571f 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2348,24 +2348,15 @@ static int cal_probe(struct platform_device *pdev) platform_set_drvdata(pdev, cal); - cal->phy[0] = cal_camerarx_create(cal, 0); - if (IS_ERR(cal->phy[0])) - return PTR_ERR(cal->phy[0]); - - if (cal->data->num_csi2_phy > 1) { - cal->phy[1] = cal_camerarx_create(cal, 1); - if (IS_ERR(cal->phy[1])) - return PTR_ERR(cal->phy[1]); - } else { - cal->phy[1] = NULL; + for (i = 0; i < cal->data->num_csi2_phy; ++i) { + cal->phy[i] = cal_camerarx_create(cal, i); + if (IS_ERR(cal->phy[i])) + return PTR_ERR(cal->phy[i]); } - cal->ctx[0] = NULL; - cal->ctx[1] = NULL; + for (i = 0; i < cal->data->num_csi2_phy; ++i) + cal->ctx[i] = cal_create_instance(cal, i); - cal->ctx[0] = cal_create_instance(cal, 0); - if (cal->data->num_csi2_phy > 1) - cal->ctx[1] = cal_create_instance(cal, 1); if (!cal->ctx[0] && !cal->ctx[1]) { cal_err(cal, "Neither port is configured, no point in staying up\n"); return -ENODEV; -- cgit v1.2.3 From 4607c2487bf21846b849878f904ff80755158aa3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:04 +0200 Subject: media: ti-vpe: cal: Drop struct cal_dev v4l2_dev field The v4l2_dev field of the cal_dev structure is initialized but never used. Drop it. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index f92d6e49571f..feb53b862306 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -279,7 +279,6 @@ struct cal_dev { void __iomem *base; struct resource *res; struct platform_device *pdev; - struct v4l2_device v4l2_dev; const struct cal_data *data; @@ -2288,10 +2287,6 @@ static int cal_probe(struct platform_device *pdev) return -ENODEV; } - /* set pseudo v4l2 device name so we can use v4l2_printk */ - strscpy(cal->v4l2_dev.name, CAL_MODULE_NAME, - sizeof(cal->v4l2_dev.name)); - /* save pdev pointer */ cal->pdev = pdev; -- cgit v1.2.3 From 573e298cfe679490a43e6d439885b79f5bd63e23 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:05 +0200 Subject: media: ti-vpe: cal: Split CAMERARX syscon regmap retrieval to a function Simplify the cal_probe() function by splitting the CAMERARX syscon regmap retrieval to a separate function. A few local variables are renamed in the process to shorten them (syscon_camerrx_*) or to make them more accurate (parent isn't the parent OF node but the CAL device's own OF node). Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 73 +++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 31 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index feb53b862306..6607e359855d 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -991,6 +991,45 @@ static struct regmap *cal_get_camerarx_regmap(struct cal_dev *cal) return regmap; } +static int cal_camerarx_init_regmap(struct cal_dev *cal) +{ + struct device_node *np = cal->pdev->dev.of_node; + struct regmap *syscon; + u32 syscon_offset; + int ret; + + syscon = syscon_regmap_lookup_by_phandle(np, "ti,camerrx-control"); + ret = of_property_read_u32_index(np, "ti,camerrx-control", 1, + &syscon_offset); + if (IS_ERR(syscon)) + ret = PTR_ERR(syscon); + if (ret) { + dev_warn(&cal->pdev->dev, + "failed to get ti,camerrx-control: %d\n", ret); + + /* + * Backward DTS compatibility. + * If syscon entry is not present then check if the + * camerrx_control resource is present. + */ + syscon = cal_get_camerarx_regmap(cal); + if (IS_ERR(syscon)) { + dev_err(&cal->pdev->dev, + "failed to get camerrx_control regmap\n"); + return PTR_ERR(syscon); + } + /* In this case the base already point to the direct + * CM register so no need for an offset + */ + syscon_offset = 0; + } + + cal->syscon_camerrx = syscon; + cal->syscon_camerrx_offset = syscon_offset; + + return 0; +} + /* ------------------------------------------------------------------ * Context Management * ------------------------------------------------------------------ @@ -2270,9 +2309,6 @@ static int cal_probe(struct platform_device *pdev) { struct cal_dev *cal; struct cal_ctx *ctx; - struct device_node *parent = pdev->dev.of_node; - struct regmap *syscon_camerrx; - u32 syscon_camerrx_offset; unsigned int i; int ret; int irq; @@ -2296,34 +2332,9 @@ static int cal_probe(struct platform_device *pdev) return PTR_ERR(cal->fclk); } - syscon_camerrx = syscon_regmap_lookup_by_phandle(parent, - "ti,camerrx-control"); - ret = of_property_read_u32_index(parent, "ti,camerrx-control", 1, - &syscon_camerrx_offset); - if (IS_ERR(syscon_camerrx)) - ret = PTR_ERR(syscon_camerrx); - if (ret) { - dev_warn(&pdev->dev, "failed to get ti,camerrx-control: %d\n", - ret); - - /* - * Backward DTS compatibility. - * If syscon entry is not present then check if the - * camerrx_control resource is present. - */ - syscon_camerrx = cal_get_camerarx_regmap(cal); - if (IS_ERR(syscon_camerrx)) { - dev_err(&pdev->dev, "failed to get camerrx_control regmap\n"); - return PTR_ERR(syscon_camerrx); - } - /* In this case the base already point to the direct - * CM register so no need for an offset - */ - syscon_camerrx_offset = 0; - } - - cal->syscon_camerrx = syscon_camerrx; - cal->syscon_camerrx_offset = syscon_camerrx_offset; + ret = cal_camerarx_init_regmap(cal); + if (ret < 0) + return ret; cal->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cal_top"); -- cgit v1.2.3 From 2cabb7ade7a0a1c8da62ba4ed18e71a7d54e7c62 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:06 +0200 Subject: media: ti-vpe: cal: Use syscon_regmap_lookup_by_phandle_args() Use the syscon_regmap_lookup_by_phandle_args() function to replace manual lookup of the syscon regmap offset. This simplifies the cal_camerarx_init_regmap() implementation. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 6607e359855d..cb567d313035 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -995,17 +995,14 @@ static int cal_camerarx_init_regmap(struct cal_dev *cal) { struct device_node *np = cal->pdev->dev.of_node; struct regmap *syscon; - u32 syscon_offset; - int ret; + unsigned int offset; - syscon = syscon_regmap_lookup_by_phandle(np, "ti,camerrx-control"); - ret = of_property_read_u32_index(np, "ti,camerrx-control", 1, - &syscon_offset); - if (IS_ERR(syscon)) - ret = PTR_ERR(syscon); - if (ret) { + syscon = syscon_regmap_lookup_by_phandle_args(np, "ti,camerrx-control", + 1, &offset); + if (IS_ERR(syscon)) { dev_warn(&cal->pdev->dev, - "failed to get ti,camerrx-control: %d\n", ret); + "failed to get ti,camerrx-control: %ld\n", + PTR_ERR(syscon)); /* * Backward DTS compatibility. @@ -1021,11 +1018,11 @@ static int cal_camerarx_init_regmap(struct cal_dev *cal) /* In this case the base already point to the direct * CM register so no need for an offset */ - syscon_offset = 0; + offset = 0; } cal->syscon_camerrx = syscon; - cal->syscon_camerrx_offset = syscon_offset; + cal->syscon_camerrx_offset = offset; return 0; } -- cgit v1.2.3 From 6963e46ce61388c05ae26918ce3afc138ac5c9f8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:07 +0200 Subject: media: ti-vpe: cal: Inline cal_get_camerarx_regmap() in caller The cal_get_camerarx_regmap() function is called in a single place. Inline it in its caller, as it results in a clear code flow. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 75 ++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 43 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index cb567d313035..aecded3cad16 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -960,20 +960,36 @@ static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, return phy; } -static struct regmap *cal_get_camerarx_regmap(struct cal_dev *cal) +static int cal_camerarx_init_regmap(struct cal_dev *cal) { - struct platform_device *pdev = cal->pdev; + struct device_node *np = cal->pdev->dev.of_node; struct regmap_config config = { }; - struct regmap *regmap; - void __iomem *base; + struct regmap *syscon; struct resource *res; + unsigned int offset; + void __iomem *base; + + syscon = syscon_regmap_lookup_by_phandle_args(np, "ti,camerrx-control", + 1, &offset); + if (!IS_ERR(syscon)) { + cal->syscon_camerrx = syscon; + cal->syscon_camerrx_offset = offset; + return 0; + } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + dev_warn(&cal->pdev->dev, "failed to get ti,camerrx-control: %ld\n", + PTR_ERR(syscon)); + + /* + * Backward DTS compatibility. If syscon entry is not present then + * check if the camerrx_control resource is present. + */ + res = platform_get_resource_byname(cal->pdev, IORESOURCE_MEM, "camerrx_control"); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_ioremap_resource(&cal->pdev->dev, res); if (IS_ERR(base)) { - cal_err(cal, "failed to ioremap\n"); - return ERR_CAST(base); + cal_err(cal, "failed to ioremap camerrx_control\n"); + return PTR_ERR(base); } cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", @@ -984,45 +1000,18 @@ static struct regmap *cal_get_camerarx_regmap(struct cal_dev *cal) config.val_bits = 32; config.max_register = resource_size(res) - 4; - regmap = regmap_init_mmio(NULL, base, &config); - if (IS_ERR(regmap)) - pr_err("regmap init failed\n"); - - return regmap; -} - -static int cal_camerarx_init_regmap(struct cal_dev *cal) -{ - struct device_node *np = cal->pdev->dev.of_node; - struct regmap *syscon; - unsigned int offset; - - syscon = syscon_regmap_lookup_by_phandle_args(np, "ti,camerrx-control", - 1, &offset); + syscon = regmap_init_mmio(NULL, base, &config); if (IS_ERR(syscon)) { - dev_warn(&cal->pdev->dev, - "failed to get ti,camerrx-control: %ld\n", - PTR_ERR(syscon)); - - /* - * Backward DTS compatibility. - * If syscon entry is not present then check if the - * camerrx_control resource is present. - */ - syscon = cal_get_camerarx_regmap(cal); - if (IS_ERR(syscon)) { - dev_err(&cal->pdev->dev, - "failed to get camerrx_control regmap\n"); - return PTR_ERR(syscon); - } - /* In this case the base already point to the direct - * CM register so no need for an offset - */ - offset = 0; + pr_err("regmap init failed\n"); + return PTR_ERR(syscon); } + /* + * In this case the base already point to the direct CM register so no + * need for an offset. + */ cal->syscon_camerrx = syscon; - cal->syscon_camerrx_offset = offset; + cal->syscon_camerrx_offset = 0; return 0; } -- cgit v1.2.3 From 214f995c20938fc19375c0806b48f0ea2dec17f3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:08 +0200 Subject: media: ti-vpe: cal: Add comments to cal_probe() to delimitate sections The cal_probe() function is a bit long, add comments to delimitate sections in order to improve readability. The platform_set_drvdata() call is moved to a more logical place as a result. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index aecded3cad16..738fd7a46399 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2309,9 +2309,10 @@ static int cal_probe(struct platform_device *pdev) return -ENODEV; } - /* save pdev pointer */ cal->pdev = pdev; + platform_set_drvdata(pdev, cal); + /* Acquire resources: clocks, CAMERARX regmap, I/O memory and IRQ. */ cal->fclk = devm_clk_get(&pdev->dev, "fck"); if (IS_ERR(cal->fclk)) { dev_err(&pdev->dev, "cannot get CAL fclk\n"); @@ -2338,14 +2339,14 @@ static int cal_probe(struct platform_device *pdev) if (ret) return ret; - platform_set_drvdata(pdev, cal); - + /* Create CAMERARX PHYs. */ for (i = 0; i < cal->data->num_csi2_phy; ++i) { cal->phy[i] = cal_camerarx_create(cal, i); if (IS_ERR(cal->phy[i])) return PTR_ERR(cal->phy[i]); } + /* Create contexts. */ for (i = 0; i < cal->data->num_csi2_phy; ++i) cal->ctx[i] = cal_create_instance(cal, i); @@ -2356,15 +2357,13 @@ static int cal_probe(struct platform_device *pdev) vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); + /* Read the revision and hardware info to verify hardware access. */ pm_runtime_enable(&pdev->dev); - ret = pm_runtime_get_sync(&pdev->dev); if (ret) goto runtime_disable; - /* Just check we can actually access the module */ cal_get_hwinfo(cal); - pm_runtime_put_sync(&pdev->dev); return 0; -- cgit v1.2.3 From 2355593496c5556ef6cecaa87d7add702882c060 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:09 +0200 Subject: media: ti-vpe: cal: Rename cal_create_instance() to cal_ctx_create() The cal_create_instance() function creates a CAL context. Rename it to cal_ctx_create() to make its purpose more explicit. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 738fd7a46399..4796e211e33d 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2204,7 +2204,7 @@ cleanup_exit: return ret; } -static struct cal_ctx *cal_create_instance(struct cal_dev *cal, int inst) +static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) { struct cal_ctx *ctx; struct v4l2_ctrl_handler *hdl; @@ -2348,7 +2348,7 @@ static int cal_probe(struct platform_device *pdev) /* Create contexts. */ for (i = 0; i < cal->data->num_csi2_phy; ++i) - cal->ctx[i] = cal_create_instance(cal, i); + cal->ctx[i] = cal_ctx_create(cal, i); if (!cal->ctx[0] && !cal->ctx[1]) { cal_err(cal, "Neither port is configured, no point in staying up\n"); -- cgit v1.2.3 From 8af2741a173408ad5ca9fb468b8f8b60495f7b20 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:10 +0200 Subject: media: ti-vpe: cal: Hardcode virtual channel to 0 Using the endpoint reg value to pass the CSI-2 source virtual channel ID in DT is a hack, isn't documented in the DT bindings, and isn't used in neither upstream DT nor TI official overlays. Hardcode the virtual channel to 0 to simplify reworking the code, proper virtual channel support will be implemented later. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 4796e211e33d..ff0367dac76a 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -326,7 +326,6 @@ struct cal_ctx { struct vb2_queue vb_vidq; unsigned int index; unsigned int cport; - unsigned int virtual_channel; /* Pointer pointing to current v4l2_buffer */ struct cal_buffer *cur_frm; @@ -1036,8 +1035,7 @@ static void cal_ctx_csi2_config(struct cal_ctx *ctx) * 0x1E: YUV422 2 pixels = 4 bytes */ set_field(&val, 0x1, CAL_CSI2_CTX_DT_MASK); - /* Virtual Channel from the CSI2 sensor usually 0! */ - set_field(&val, ctx->virtual_channel, CAL_CSI2_CTX_VC_MASK); + set_field(&val, 0, CAL_CSI2_CTX_VC_MASK); set_field(&val, ctx->v_fmt.fmt.pix.height, CAL_CSI2_CTX_LINES_MASK); set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK); set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE, @@ -2146,11 +2144,7 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst) goto cleanup_exit; } - /* Store Virtual Channel number */ - ctx->virtual_channel = endpoint->base.id; - ctx_dbg(3, ctx, "Port:%d v4l2-endpoint: CSI2\n", inst); - ctx_dbg(3, ctx, "Virtual Channel=%d\n", ctx->virtual_channel); ctx_dbg(3, ctx, "flags=0x%08x\n", endpoint->bus.mipi_csi2.flags); ctx_dbg(3, ctx, "clock_lane=%d\n", endpoint->bus.mipi_csi2.clock_lane); ctx_dbg(3, ctx, "num_data_lanes=%d\n", -- cgit v1.2.3 From cb1d4202a389511e6bc2589bd46bb6c339e4a9e8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:11 +0200 Subject: media: ti-vpe: cal: Use of_graph_get_endpoint_by_regs() to parse OF Replace the manual of_get_next_port() and of_get_next_endpoint() implementations and the corresponding logic in the caller with a call to of_graph_get_endpoint_by_regs(). This greatly simplifies OF parsing. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 114 +++--------------------------------- 1 file changed, 7 insertions(+), 107 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index ff0367dac76a..b406a56debac 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2014,125 +2014,26 @@ static const struct v4l2_async_notifier_operations cal_async_ops = { .complete = cal_async_complete, }; -static struct device_node * -of_get_next_port(const struct device_node *parent, - struct device_node *prev) -{ - struct device_node *port; - - if (!parent) - return NULL; - - if (!prev) { - struct device_node *ports; - /* - * It's the first call, we have to find a port subnode - * within this node or within an optional 'ports' node. - */ - ports = of_get_child_by_name(parent, "ports"); - if (ports) - parent = ports; - - port = of_get_child_by_name(parent, "port"); - - /* release the 'ports' node */ - of_node_put(ports); - } else { - struct device_node *ports; - - ports = of_get_parent(prev); - if (!ports) - return NULL; - - do { - port = of_get_next_child(ports, prev); - if (!port) { - of_node_put(ports); - return NULL; - } - prev = port; - } while (!of_node_name_eq(port, "port")); - of_node_put(ports); - } - - return port; -} - -static struct device_node * -of_get_next_endpoint(const struct device_node *parent, - struct device_node *prev) -{ - struct device_node *ep; - - if (!parent) - return NULL; - - do { - ep = of_get_next_child(parent, prev); - if (!ep) - return NULL; - prev = ep; - } while (!of_node_name_eq(ep, "endpoint")); - - return ep; -} - static int of_cal_create_instance(struct cal_ctx *ctx, int inst) { struct platform_device *pdev = ctx->cal->pdev; - struct device_node *ep_node, *port, *sensor_node, *parent; + struct device_node *ep_node, *sensor_node; struct v4l2_fwnode_endpoint *endpoint; struct v4l2_async_subdev *asd; - u32 regval = 0; - int ret, index, lane; - bool found_port = false; - - parent = pdev->dev.of_node; + int ret = -EINVAL, lane; endpoint = &ctx->phy->endpoint; - ep_node = NULL; - port = NULL; - sensor_node = NULL; - ret = -EINVAL; - - ctx_dbg(3, ctx, "Scanning Port node for csi2 port: %d\n", inst); - for (index = 0; index < CAL_NUM_CSI2_PORTS; index++) { - port = of_get_next_port(parent, port); - if (!port) { - ctx_dbg(1, ctx, "No port node found for csi2 port:%d\n", - index); - goto cleanup_exit; - } - - /* Match the slice number with */ - of_property_read_u32(port, "reg", ®val); - ctx_dbg(3, ctx, "port:%d inst:%d :%d\n", - index, inst, regval); - if ((regval == inst) && (index == inst)) { - found_port = true; - break; - } - } - - if (!found_port) { - ctx_dbg(1, ctx, "No port node matches csi2 port:%d\n", - inst); - goto cleanup_exit; - } - - ctx_dbg(3, ctx, "Scanning sub-device for csi2 port: %d\n", - inst); - - ep_node = of_get_next_endpoint(port, ep_node); + ctx_dbg(3, ctx, "Getting endpoint for port@%d\n", inst); + ep_node = of_graph_get_endpoint_by_regs(pdev->dev.of_node, inst, 0); if (!ep_node) { - ctx_dbg(3, ctx, "can't get next endpoint\n"); - goto cleanup_exit; + ctx_dbg(3, ctx, "Can't get endpoint\n"); + return -EINVAL; } sensor_node = of_graph_get_remote_port_parent(ep_node); if (!sensor_node) { - ctx_dbg(3, ctx, "can't get remote parent\n"); + ctx_dbg(3, ctx, "Can't get remote parent\n"); goto cleanup_exit; } @@ -2193,7 +2094,6 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst) cleanup_exit: of_node_put(sensor_node); of_node_put(ep_node); - of_node_put(port); return ret; } -- cgit v1.2.3 From b1ca64f269300d8ef4e0912f787b8f3488a1e1d6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:12 +0200 Subject: media: ti-vpe: cal: Fix usage of v4l2_fwnode_endpoint_parse() Letting the v4l2_fwnode_endpoint_parse() function determine the bus type automatically is deprecated. Set the bus type to DPHY manually as the TI CAL only supports DPHY. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index b406a56debac..8de4d7b96a22 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2037,11 +2037,10 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst) goto cleanup_exit; } - v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint); - - if (endpoint->bus_type != V4L2_MBUS_CSI2_DPHY) { - ctx_err(ctx, "Port:%d sub-device %pOFn is not a CSI2 device\n", - inst, sensor_node); + endpoint->bus_type = V4L2_MBUS_CSI2_DPHY; + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint); + if (ret < 0) { + ctx_err(ctx, "Failed to parse endpoint\n"); goto cleanup_exit; } -- cgit v1.2.3 From 4b71bfbc9787e359f63ef0d876e173b1c474ffc7 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:13 +0200 Subject: media: ti-vpe: cal: Decouple control handler from v4l2_device To prepare for decoupling the v4l2_device from the cal_ctx, don't set the control handler in the v4l2_device and expect the video node to use it automatically, but set the video node control handler directly. This requires adding the sensor subdev controls to the control handler manually, as that operation was performed on the v4l2_device by v4l2_device_register_subdev(). Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 8de4d7b96a22..fe96edebd9c2 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1871,6 +1871,7 @@ static const struct video_device cal_videodev = { static int cal_complete_ctx(struct cal_ctx *ctx) { + struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; struct video_device *vfd; struct vb2_queue *q; int ret; @@ -1904,6 +1905,17 @@ static int cal_complete_ctx(struct cal_ctx *ctx) vfd->v4l2_dev = &ctx->v4l2_dev; vfd->queue = q; + /* Initialize the control handler. */ + v4l2_ctrl_handler_init(hdl, 11); + v4l2_ctrl_add_handler(hdl, ctx->phy->sensor->ctrl_handler, NULL, true); + if (hdl->error) { + ctx_err(ctx, "Failed to init ctrl handler\n"); + ret = hdl->error; + goto error; + } + + vfd->ctrl_handler = hdl; + /* * Provide a mutex to v4l2 core. It will be used to protect * all fops and v4l2 ioctls. @@ -1913,12 +1925,16 @@ static int cal_complete_ctx(struct cal_ctx *ctx) ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr); if (ret < 0) - return ret; + goto error; ctx_info(ctx, "V4L2 device registered as %s\n", video_device_node_name(vfd)); return 0; + +error: + v4l2_ctrl_handler_free(hdl); + return ret; } static int cal_async_bound(struct v4l2_async_notifier *notifier, @@ -2100,7 +2116,6 @@ cleanup_exit: static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) { struct cal_ctx *ctx; - struct v4l2_ctrl_handler *hdl; int ret; ctx = devm_kzalloc(&cal->pdev->dev, sizeof(*ctx), GFP_KERNEL); @@ -2114,15 +2129,7 @@ static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) "%s-%03d", CAL_MODULE_NAME, inst); ret = v4l2_device_register(&cal->pdev->dev, &ctx->v4l2_dev); if (ret) - goto err_exit; - - hdl = &ctx->ctrl_handler; - ret = v4l2_ctrl_handler_init(hdl, 11); - if (ret) { - ctx_err(ctx, "Failed to init ctrl handler\n"); - goto unreg_dev; - } - ctx->v4l2_dev.ctrl_handler = hdl; + return NULL; /* Make sure Camera Core H/W register area is available */ ctx->phy = cal->phy[inst]; @@ -2134,15 +2141,12 @@ static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) ret = of_cal_create_instance(ctx, inst); if (ret) { ret = -EINVAL; - goto free_hdl; + goto unreg_dev; } return ctx; -free_hdl: - v4l2_ctrl_handler_free(hdl); unreg_dev: v4l2_device_unregister(&ctx->v4l2_dev); -err_exit: return NULL; } -- cgit v1.2.3 From b4b784953c49c01ef0a52b7e81a938bb4bbf53d5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:14 +0200 Subject: media: ti-vpe: cal: Move v4l2_device from cal_ctx to cal_dev The v4l2_device structure is meant to represent the whole device. In the CAL case, this corresponds to the CAL, the CAMERARX instances and the connected sensors. There should thus be a single v4l2_device instance. Replace the per-context instance with a global instance in the cal_dev structure. Don't set the v4l2_device name manually as v4l2_device_register() sets it to a value that is suitable for the driver. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 44 ++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index fe96edebd9c2..e747d8eb96aa 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -290,13 +290,14 @@ struct cal_dev { struct cal_camerarx *phy[CAL_NUM_CSI2_PORTS]; struct cal_ctx *ctx[CAL_NUM_CONTEXT]; + + struct v4l2_device v4l2_dev; }; /* * There is one cal_ctx structure for each camera core context. */ struct cal_ctx { - struct v4l2_device v4l2_dev; struct v4l2_ctrl_handler ctrl_handler; struct video_device vdev; struct v4l2_async_notifier notifier; @@ -1902,7 +1903,7 @@ static int cal_complete_ctx(struct cal_ctx *ctx) vfd = &ctx->vdev; *vfd = cal_videodev; - vfd->v4l2_dev = &ctx->v4l2_dev; + vfd->v4l2_dev = &ctx->cal->v4l2_dev; vfd->queue = q; /* Initialize the control handler. */ @@ -2091,7 +2092,7 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst) } ctx->notifier.ops = &cal_async_ops; - ret = v4l2_async_notifier_register(&ctx->v4l2_dev, + ret = v4l2_async_notifier_register(&ctx->cal->v4l2_dev, &ctx->notifier); if (ret) { ctx_err(ctx, "Error registering async notifier\n"); @@ -2125,12 +2126,6 @@ static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) /* save the cal_dev * for future ref */ ctx->cal = cal; - snprintf(ctx->v4l2_dev.name, sizeof(ctx->v4l2_dev.name), - "%s-%03d", CAL_MODULE_NAME, inst); - ret = v4l2_device_register(&cal->pdev->dev, &ctx->v4l2_dev); - if (ret) - return NULL; - /* Make sure Camera Core H/W register area is available */ ctx->phy = cal->phy[inst]; @@ -2139,15 +2134,10 @@ static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) ctx->cport = inst; ret = of_cal_create_instance(ctx, inst); - if (ret) { - ret = -EINVAL; - goto unreg_dev; - } - return ctx; + if (ret) + return NULL; -unreg_dev: - v4l2_device_unregister(&ctx->v4l2_dev); - return NULL; + return ctx; } static const struct of_device_id cal_of_match[] = { @@ -2243,13 +2233,21 @@ static int cal_probe(struct platform_device *pdev) return PTR_ERR(cal->phy[i]); } + /* Register the V4L2 device. */ + ret = v4l2_device_register(&pdev->dev, &cal->v4l2_dev); + if (ret) { + cal_err(cal, "Failed to register V4L2 device\n"); + return ret; + } + /* Create contexts. */ for (i = 0; i < cal->data->num_csi2_phy; ++i) cal->ctx[i] = cal_ctx_create(cal, i); if (!cal->ctx[0] && !cal->ctx[1]) { cal_err(cal, "Neither port is configured, no point in staying up\n"); - return -ENODEV; + ret = -ENODEV; + goto error_v4l2; } vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); @@ -2258,14 +2256,14 @@ static int cal_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); ret = pm_runtime_get_sync(&pdev->dev); if (ret) - goto runtime_disable; + goto error_pm_runtime; cal_get_hwinfo(cal); pm_runtime_put_sync(&pdev->dev); return 0; -runtime_disable: +error_pm_runtime: vb2_dma_contig_clear_max_seg_size(&pdev->dev); pm_runtime_disable(&pdev->dev); @@ -2275,10 +2273,11 @@ runtime_disable: v4l2_async_notifier_unregister(&ctx->notifier); v4l2_async_notifier_cleanup(&ctx->notifier); v4l2_ctrl_handler_free(&ctx->ctrl_handler); - v4l2_device_unregister(&ctx->v4l2_dev); } } +error_v4l2: + v4l2_device_unregister(&cal->v4l2_dev); return ret; } @@ -2301,11 +2300,12 @@ static int cal_remove(struct platform_device *pdev) v4l2_async_notifier_unregister(&ctx->notifier); v4l2_async_notifier_cleanup(&ctx->notifier); v4l2_ctrl_handler_free(&ctx->ctrl_handler); - v4l2_device_unregister(&ctx->v4l2_dev); video_unregister_device(&ctx->vdev); } } + v4l2_device_unregister(&cal->v4l2_dev); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); -- cgit v1.2.3 From d68a94e98a89230750b8dfb667c71c3b4e0a02ed Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:15 +0200 Subject: media: ti-vpe: cal: Split video device initialization and registration There's no need to delay most of the video device initialization until the sensor subdevs are bound. Split the initialization and registration, and perform the initialization when creating the context. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 70 ++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 28 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index e747d8eb96aa..447ba33c4337 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1856,7 +1856,7 @@ static const struct vb2_ops cal_video_qops = { }; /* ------------------------------------------------------------------ - * Initialization and module stuff + * V4L2 Initialization and Registration * ------------------------------------------------------------------ */ @@ -1870,11 +1870,36 @@ static const struct video_device cal_videodev = { V4L2_CAP_READWRITE, }; -static int cal_complete_ctx(struct cal_ctx *ctx) +static int cal_ctx_v4l2_register(struct cal_ctx *ctx) { struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; - struct video_device *vfd; - struct vb2_queue *q; + struct video_device *vfd = &ctx->vdev; + int ret; + + ret = v4l2_ctrl_add_handler(hdl, ctx->phy->sensor->ctrl_handler, NULL, + true); + if (ret < 0) { + ctx_err(ctx, "Failed to add sensor ctrl handler\n"); + return ret; + } + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr); + if (ret < 0) { + ctx_err(ctx, "Failed to register video device\n"); + return ret; + } + + ctx_info(ctx, "V4L2 device registered as %s\n", + video_device_node_name(vfd)); + + return 0; +} + +static int cal_ctx_v4l2_init(struct cal_ctx *ctx) +{ + struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; + struct video_device *vfd = &ctx->vdev; + struct vb2_queue *q = &ctx->vb_vidq; int ret; /* initialize locks */ @@ -1882,7 +1907,6 @@ static int cal_complete_ctx(struct cal_ctx *ctx) mutex_init(&ctx->mutex); /* initialize queue */ - q = &ctx->vb_vidq; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; q->drv_priv = ctx; @@ -1901,18 +1925,15 @@ static int cal_complete_ctx(struct cal_ctx *ctx) /* init video dma queues */ INIT_LIST_HEAD(&ctx->vidq.active); - vfd = &ctx->vdev; *vfd = cal_videodev; vfd->v4l2_dev = &ctx->cal->v4l2_dev; vfd->queue = q; /* Initialize the control handler. */ - v4l2_ctrl_handler_init(hdl, 11); - v4l2_ctrl_add_handler(hdl, ctx->phy->sensor->ctrl_handler, NULL, true); - if (hdl->error) { + ret = v4l2_ctrl_handler_init(hdl, 11); + if (ret < 0) { ctx_err(ctx, "Failed to init ctrl handler\n"); - ret = hdl->error; - goto error; + return ret; } vfd->ctrl_handler = hdl; @@ -1924,20 +1945,14 @@ static int cal_complete_ctx(struct cal_ctx *ctx) vfd->lock = &ctx->mutex; video_set_drvdata(vfd, ctx); - ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr); - if (ret < 0) - goto error; - - ctx_info(ctx, "V4L2 device registered as %s\n", - video_device_node_name(vfd)); - return 0; - -error: - v4l2_ctrl_handler_free(hdl); - return ret; } +/* ------------------------------------------------------------------ + * Initialization and module stuff + * ------------------------------------------------------------------ + */ + static int cal_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) @@ -1992,7 +2007,7 @@ static int cal_async_bound(struct v4l2_async_notifier *notifier, return -EINVAL; } - cal_complete_ctx(ctx); + cal_ctx_v4l2_register(ctx); return 0; } @@ -2123,16 +2138,15 @@ static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) if (!ctx) return NULL; - /* save the cal_dev * for future ref */ ctx->cal = cal; - - /* Make sure Camera Core H/W register area is available */ ctx->phy = cal->phy[inst]; - - /* Store the instance id */ ctx->index = inst; ctx->cport = inst; + ret = cal_ctx_v4l2_init(ctx); + if (ret) + return NULL; + ret = of_cal_create_instance(ctx, inst); if (ret) return NULL; -- cgit v1.2.3 From 19a2374068b557ed1a19c68ebaef5f1a161e2973 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:16 +0200 Subject: media: ti-vpe: cal: Add context V4L2 cleanup and unregister functions Create cal_ctx_v4l2_unregister() and cal_ctx_v4l2_cleanup() functions to unregister and cleanup the V4L2-related objects from the context, and call them in cal_remove() and in the error path of cal_probe(). Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 447ba33c4337..5f218ac5fb60 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1895,6 +1895,11 @@ static int cal_ctx_v4l2_register(struct cal_ctx *ctx) return 0; } +static void cal_ctx_v4l2_unregister(struct cal_ctx *ctx) +{ + video_unregister_device(&ctx->vdev); +} + static int cal_ctx_v4l2_init(struct cal_ctx *ctx) { struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; @@ -1948,6 +1953,11 @@ static int cal_ctx_v4l2_init(struct cal_ctx *ctx) return 0; } +static void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx) +{ + v4l2_ctrl_handler_free(&ctx->ctrl_handler); +} + /* ------------------------------------------------------------------ * Initialization and module stuff * ------------------------------------------------------------------ @@ -2286,7 +2296,7 @@ error_pm_runtime: if (ctx) { v4l2_async_notifier_unregister(&ctx->notifier); v4l2_async_notifier_cleanup(&ctx->notifier); - v4l2_ctrl_handler_free(&ctx->ctrl_handler); + cal_ctx_v4l2_cleanup(ctx); } } @@ -2313,8 +2323,8 @@ static int cal_remove(struct platform_device *pdev) cal_camerarx_disable(ctx->phy); v4l2_async_notifier_unregister(&ctx->notifier); v4l2_async_notifier_cleanup(&ctx->notifier); - v4l2_ctrl_handler_free(&ctx->ctrl_handler); - video_unregister_device(&ctx->vdev); + cal_ctx_v4l2_cleanup(ctx); + cal_ctx_v4l2_unregister(ctx); } } -- cgit v1.2.3 From 6675e871ca4dab14e849298767d34c2722f3ee1e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:17 +0200 Subject: media: ti-vpe: cal: Unregister video device before cleanup In cal_remove(), unregister the video devices as the first operation, before cleaning up the V4L2 objects, to avoid use-after-free. This isn't a complete solution yet, as video nodes can be kept open across unregistration. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 5f218ac5fb60..5580913a1356 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2320,11 +2320,11 @@ static int cal_remove(struct platform_device *pdev) if (ctx) { ctx_dbg(1, ctx, "unregistering %s\n", video_device_node_name(&ctx->vdev)); + cal_ctx_v4l2_unregister(ctx); cal_camerarx_disable(ctx->phy); v4l2_async_notifier_unregister(&ctx->notifier); v4l2_async_notifier_cleanup(&ctx->notifier); cal_ctx_v4l2_cleanup(ctx); - cal_ctx_v4l2_unregister(ctx); } } -- cgit v1.2.3 From d3665f3b7c08bc50f97b2f308f4b745f7e80b3e8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:18 +0200 Subject: media: ti-vpe: cal: Add cal_camerarx_destroy() to cleanup CAMERARX The cal_camerarx_create() function allocates resources with devm_*, and thus doesn't need any manual cleanup. Those won't hold true for long, as we will need to store resources that have no devm_* allocation variant in cal_camerarx. Furthermore, devm_kzalloc() is the wrong memory allocation API for structures that can be accessed from userspace, as device nodes can be kept open across device removal. Add a cal_camerarx_destroy() function to destroy a CAMERARX instance explicitly, and switch to kzalloc() for memory allocation. Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 5580913a1356..492141f07d69 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -932,7 +932,7 @@ static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, struct cal_camerarx *phy; int ret; - phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); + phy = kzalloc(sizeof(*phy), GFP_KERNEL); if (!phy) return ERR_PTR(-ENOMEM); @@ -947,7 +947,8 @@ static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, phy->base = devm_ioremap_resource(&pdev->dev, phy->res); if (IS_ERR(phy->base)) { cal_err(cal, "failed to ioremap\n"); - return ERR_CAST(phy->base); + ret = PTR_ERR(phy->base); + goto error; } cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", @@ -955,9 +956,21 @@ static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, ret = cal_camerarx_regmap_init(cal, phy); if (ret) - return ERR_PTR(ret); + goto error; return phy; + +error: + kfree(phy); + return ERR_PTR(ret); +} + +static void cal_camerarx_destroy(struct cal_camerarx *phy) +{ + if (!phy) + return; + + kfree(phy); } static int cal_camerarx_init_regmap(struct cal_dev *cal) @@ -2253,15 +2266,18 @@ static int cal_probe(struct platform_device *pdev) /* Create CAMERARX PHYs. */ for (i = 0; i < cal->data->num_csi2_phy; ++i) { cal->phy[i] = cal_camerarx_create(cal, i); - if (IS_ERR(cal->phy[i])) - return PTR_ERR(cal->phy[i]); + if (IS_ERR(cal->phy[i])) { + ret = PTR_ERR(cal->phy[i]); + cal->phy[i] = NULL; + goto error_camerarx; + } } /* Register the V4L2 device. */ ret = v4l2_device_register(&pdev->dev, &cal->v4l2_dev); if (ret) { cal_err(cal, "Failed to register V4L2 device\n"); - return ret; + goto error_camerarx; } /* Create contexts. */ @@ -2302,6 +2318,11 @@ error_pm_runtime: error_v4l2: v4l2_device_unregister(&cal->v4l2_dev); + +error_camerarx: + for (i = 0; i < ARRAY_SIZE(cal->phy); i++) + cal_camerarx_destroy(cal->phy[i]); + return ret; } @@ -2330,6 +2351,9 @@ static int cal_remove(struct platform_device *pdev) v4l2_device_unregister(&cal->v4l2_dev); + for (i = 0; i < ARRAY_SIZE(cal->phy); i++) + cal_camerarx_destroy(cal->phy[i]); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); -- cgit v1.2.3 From f5ddf19eca33281d8aabb8d802bed5a5658bbcb6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:19 +0200 Subject: media: ti-vpe: cal: Move DT parsing to CAMERARX The DT port nodes corresponding to the CSI-2 inputs belong to the CAMERARX instances. Move parsing of the DT properties to a new cal_camerarx_parse_dt() function, called by cal_camerarx_create(). Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 185 +++++++++++++++++++++--------------- 1 file changed, 111 insertions(+), 74 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 492141f07d69..a2c245ad23be 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -269,6 +269,7 @@ struct cal_camerarx { unsigned int instance; struct v4l2_fwnode_endpoint endpoint; + struct device_node *sensor_node; struct v4l2_subdev *sensor; unsigned int external_rate; }; @@ -925,6 +926,73 @@ static int cal_camerarx_regmap_init(struct cal_dev *cal, return 0; } +static int cal_camerarx_parse_dt(struct cal_camerarx *phy) +{ + struct v4l2_fwnode_endpoint *endpoint = &phy->endpoint; + struct platform_device *pdev = phy->cal->pdev; + struct device_node *ep_node; + char data_lanes[V4L2_FWNODE_CSI2_MAX_DATA_LANES * 2]; + unsigned int i; + int ret; + + /* + * Find the endpoint node for the port corresponding to the PHY + * instance, and parse its CSI-2-related properties. + */ + ep_node = of_graph_get_endpoint_by_regs(pdev->dev.of_node, + phy->instance, 0); + if (!ep_node) { + /* + * The endpoint is not mandatory, not all PHY instances need to + * be connected in DT. + */ + phy_dbg(3, phy, "Port has no endpoint\n"); + return 0; + } + + endpoint->bus_type = V4L2_MBUS_CSI2_DPHY; + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint); + if (ret < 0) { + phy_err(phy, "Failed to parse endpoint\n"); + goto done; + } + + for (i = 0; i < endpoint->bus.mipi_csi2.num_data_lanes; i++) { + unsigned int lane = endpoint->bus.mipi_csi2.data_lanes[i]; + + if (lane > 4) { + phy_err(phy, "Invalid position %u for data lane %u\n", + lane, i); + ret = -EINVAL; + goto done; + } + + data_lanes[i*2] = '0' + lane; + data_lanes[i*2+1] = ' '; + } + + data_lanes[i*2-1] = '\0'; + + phy_dbg(3, phy, + "CSI-2 bus: clock lane <%u>, data lanes <%s>, flags 0x%08x\n", + endpoint->bus.mipi_csi2.clock_lane, data_lanes, + endpoint->bus.mipi_csi2.flags); + + /* Retrieve the connected device and store it for later use. */ + phy->sensor_node = of_graph_get_remote_port_parent(ep_node); + if (!phy->sensor_node) { + phy_dbg(3, phy, "Can't get remote parent\n"); + ret = -EINVAL; + goto done; + } + + phy_dbg(1, phy, "Found connected device %pOFn\n", phy->sensor_node); + +done: + of_node_put(ep_node); + return ret; +} + static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, unsigned int instance) { @@ -958,6 +1026,10 @@ static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, if (ret) goto error; + ret = cal_camerarx_parse_dt(phy); + if (ret) + goto error; + return phy; error: @@ -970,6 +1042,7 @@ static void cal_camerarx_destroy(struct cal_camerarx *phy) if (!phy) return; + of_node_put(phy->sensor_node); kfree(phy); } @@ -1972,7 +2045,7 @@ static void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx) } /* ------------------------------------------------------------------ - * Initialization and module stuff + * Asynchronous V4L2 subdev binding * ------------------------------------------------------------------ */ @@ -2071,87 +2144,37 @@ static const struct v4l2_async_notifier_operations cal_async_ops = { static int of_cal_create_instance(struct cal_ctx *ctx, int inst) { - struct platform_device *pdev = ctx->cal->pdev; - struct device_node *ep_node, *sensor_node; - struct v4l2_fwnode_endpoint *endpoint; struct v4l2_async_subdev *asd; - int ret = -EINVAL, lane; - - endpoint = &ctx->phy->endpoint; - - ctx_dbg(3, ctx, "Getting endpoint for port@%d\n", inst); - ep_node = of_graph_get_endpoint_by_regs(pdev->dev.of_node, inst, 0); - if (!ep_node) { - ctx_dbg(3, ctx, "Can't get endpoint\n"); - return -EINVAL; - } - - sensor_node = of_graph_get_remote_port_parent(ep_node); - if (!sensor_node) { - ctx_dbg(3, ctx, "Can't get remote parent\n"); - goto cleanup_exit; - } - - endpoint->bus_type = V4L2_MBUS_CSI2_DPHY; - ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint); - if (ret < 0) { - ctx_err(ctx, "Failed to parse endpoint\n"); - goto cleanup_exit; - } - - ctx_dbg(3, ctx, "Port:%d v4l2-endpoint: CSI2\n", inst); - ctx_dbg(3, ctx, "flags=0x%08x\n", endpoint->bus.mipi_csi2.flags); - ctx_dbg(3, ctx, "clock_lane=%d\n", endpoint->bus.mipi_csi2.clock_lane); - ctx_dbg(3, ctx, "num_data_lanes=%d\n", - endpoint->bus.mipi_csi2.num_data_lanes); - ctx_dbg(3, ctx, "data_lanes= <\n"); - for (lane = 0; lane < endpoint->bus.mipi_csi2.num_data_lanes; lane++) - ctx_dbg(3, ctx, "\t%d\n", - endpoint->bus.mipi_csi2.data_lanes[lane]); - ctx_dbg(3, ctx, "\t>\n"); - - ctx_dbg(1, ctx, "Port: %d found sub-device %pOFn\n", - inst, sensor_node); + struct fwnode_handle *fwnode; + int ret; v4l2_async_notifier_init(&ctx->notifier); + ctx->notifier.ops = &cal_async_ops; - asd = kzalloc(sizeof(*asd), GFP_KERNEL); - if (!asd) - goto cleanup_exit; - - asd->match_type = V4L2_ASYNC_MATCH_FWNODE; - asd->match.fwnode = of_fwnode_handle(sensor_node); - - ret = v4l2_async_notifier_add_subdev(&ctx->notifier, asd); - if (ret) { - ctx_err(ctx, "Error adding asd\n"); - kfree(asd); - goto cleanup_exit; + fwnode = of_fwnode_handle(ctx->phy->sensor_node); + asd = v4l2_async_notifier_add_fwnode_subdev(&ctx->notifier, fwnode, + sizeof(*asd)); + if (IS_ERR(asd)) { + ctx_err(ctx, "Failed to add subdev to notifier\n"); + return PTR_ERR(asd); } - ctx->notifier.ops = &cal_async_ops; ret = v4l2_async_notifier_register(&ctx->cal->v4l2_dev, &ctx->notifier); if (ret) { ctx_err(ctx, "Error registering async notifier\n"); v4l2_async_notifier_cleanup(&ctx->notifier); - ret = -EINVAL; + return ret; } - /* - * On success we need to keep reference on sensor_node, or - * if notifier_cleanup was called above, sensor_node was - * already put. - */ - sensor_node = NULL; - -cleanup_exit: - of_node_put(sensor_node); - of_node_put(ep_node); - - return ret; + return 0; } +/* ------------------------------------------------------------------ + * Initialization and module stuff + * ------------------------------------------------------------------ + */ + static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) { struct cal_ctx *ctx; @@ -2219,6 +2242,7 @@ static int cal_probe(struct platform_device *pdev) { struct cal_dev *cal; struct cal_ctx *ctx; + bool connected = false; unsigned int i; int ret; int irq; @@ -2271,6 +2295,15 @@ static int cal_probe(struct platform_device *pdev) cal->phy[i] = NULL; goto error_camerarx; } + + if (cal->phy[i]->sensor_node) + connected = true; + } + + if (!connected) { + cal_err(cal, "Neither port is configured, no point in staying up\n"); + ret = -ENODEV; + goto error_camerarx; } /* Register the V4L2 device. */ @@ -2281,13 +2314,16 @@ static int cal_probe(struct platform_device *pdev) } /* Create contexts. */ - for (i = 0; i < cal->data->num_csi2_phy; ++i) - cal->ctx[i] = cal_ctx_create(cal, i); + for (i = 0; i < cal->data->num_csi2_phy; ++i) { + if (!cal->phy[i]->sensor_node) + continue; - if (!cal->ctx[0] && !cal->ctx[1]) { - cal_err(cal, "Neither port is configured, no point in staying up\n"); - ret = -ENODEV; - goto error_v4l2; + cal->ctx[i] = cal_ctx_create(cal, i); + if (!cal->ctx[i]) { + cal_err(cal, "Failed to create context %u\n", i); + ret = -ENODEV; + goto error_context; + } } vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); @@ -2307,6 +2343,8 @@ error_pm_runtime: vb2_dma_contig_clear_max_seg_size(&pdev->dev); pm_runtime_disable(&pdev->dev); + +error_context: for (i = 0; i < CAL_NUM_CONTEXT; i++) { ctx = cal->ctx[i]; if (ctx) { @@ -2316,7 +2354,6 @@ error_pm_runtime: } } -error_v4l2: v4l2_device_unregister(&cal->v4l2_dev); error_camerarx: -- cgit v1.2.3 From 4d04c23fadc67acc47f26f7143e15c81b0143f47 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:20 +0200 Subject: media: ti-vpe: cal: Use ARRAY_SIZE to replace numerical value Use ARRAY_SIZE(cal->ctx) to iterate over the cal->ctx array instead of using the numerical value from the CAL_NUM_CONTEXT macro (or, worse, hardcoding the value 2). This will allow reworking contexts with less changes in the code. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index a2c245ad23be..e91788519bb4 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1314,7 +1314,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) /* Clear Interrupt status */ reg_write(cal, CAL_HL_IRQSTATUS(1), status); - for (i = 0; i < 2; ++i) { + for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { if (isportirqset(status, i)) { ctx = cal->ctx[i]; @@ -1337,7 +1337,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) /* Clear Interrupt status */ reg_write(cal, CAL_HL_IRQSTATUS(2), status); - for (i = 0; i < 2; ++i) { + for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { if (isportirqset(status, i)) { ctx = cal->ctx[i]; dma_q = &ctx->vidq; @@ -2345,7 +2345,7 @@ error_pm_runtime: pm_runtime_disable(&pdev->dev); error_context: - for (i = 0; i < CAL_NUM_CONTEXT; i++) { + for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { ctx = cal->ctx[i]; if (ctx) { v4l2_async_notifier_unregister(&ctx->notifier); @@ -2373,7 +2373,7 @@ static int cal_remove(struct platform_device *pdev) pm_runtime_get_sync(&pdev->dev); - for (i = 0; i < CAL_NUM_CONTEXT; i++) { + for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { ctx = cal->ctx[i]; if (ctx) { ctx_dbg(1, ctx, "unregistering %s\n", -- cgit v1.2.3 From a439e57358ee1562257668d77b19f8f1a629a1fd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:21 +0200 Subject: media: ti-vpe: cal: Move all sensor-related init to .bound() notifier The initialization of the context based on the connected sensor is split between the async notifier .bound() and .complete() operations. Move it all to a separate function and call it from .bound() operation to prepare for the move of the notifiers from the contexts to the cal_dev. Only V4L2 registration is kept in the .complete() operation. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 132 +++++++++++++++++++----------------- 1 file changed, 68 insertions(+), 64 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index e91788519bb4..b5eb6a640d70 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1986,6 +1986,72 @@ static void cal_ctx_v4l2_unregister(struct cal_ctx *ctx) video_unregister_device(&ctx->vdev); } +static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) +{ + struct v4l2_subdev_mbus_code_enum mbus_code; + struct v4l2_mbus_framefmt mbus_fmt; + const struct cal_fmt *fmt; + unsigned int i, j, k; + int ret = 0; + + /* Enumerate sub device formats and enable all matching local formats */ + ctx->num_active_fmt = 0; + for (j = 0, i = 0; ret != -EINVAL; ++j) { + + memset(&mbus_code, 0, sizeof(mbus_code)); + mbus_code.index = j; + mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_mbus_code, + NULL, &mbus_code); + if (ret) + continue; + + ctx_dbg(2, ctx, + "subdev %s: code: %04x idx: %u\n", + ctx->phy->sensor->name, mbus_code.code, j); + + for (k = 0; k < ARRAY_SIZE(cal_formats); k++) { + const struct cal_fmt *fmt = &cal_formats[k]; + + if (mbus_code.code == fmt->code) { + ctx->active_fmt[i] = fmt; + ctx_dbg(2, ctx, + "matched fourcc: %s: code: %04x idx: %u\n", + fourcc_to_str(fmt->fourcc), + fmt->code, i); + ctx->num_active_fmt = ++i; + } + } + } + + if (i == 0) { + ctx_err(ctx, "No suitable format reported by subdev %s\n", + ctx->phy->sensor->name); + return -EINVAL; + } + + ret = __subdev_get_format(ctx, &mbus_fmt); + if (ret) + return ret; + + fmt = find_format_by_code(ctx, mbus_fmt.code); + if (!fmt) { + ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n", + mbus_fmt.code); + return -EINVAL; + } + + /* Save current subdev format */ + v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); + ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; + cal_calc_format_size(ctx, fmt, &ctx->v_fmt); + ctx->fmt = fmt; + ctx->m_fmt = mbus_fmt; + + return 0; +} + static int cal_ctx_v4l2_init(struct cal_ctx *ctx) { struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; @@ -2054,9 +2120,6 @@ static int cal_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_async_subdev *asd) { struct cal_ctx *ctx = notifier_to_ctx(notifier); - struct v4l2_subdev_mbus_code_enum mbus_code; - unsigned int i, j, k; - int ret = 0; if (ctx->phy->sensor) { ctx_info(ctx, "Rejecting subdev %s (Already set!!)", @@ -2067,73 +2130,14 @@ static int cal_async_bound(struct v4l2_async_notifier *notifier, ctx->phy->sensor = subdev; ctx_dbg(1, ctx, "Using sensor %s for capture\n", subdev->name); - /* Enumerate sub device formats and enable all matching local formats */ - ctx->num_active_fmt = 0; - for (j = 0, i = 0; ret != -EINVAL; ++j) { - - memset(&mbus_code, 0, sizeof(mbus_code)); - mbus_code.index = j; - mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(subdev, pad, enum_mbus_code, - NULL, &mbus_code); - if (ret) - continue; - - ctx_dbg(2, ctx, - "subdev %s: code: %04x idx: %u\n", - subdev->name, mbus_code.code, j); - - for (k = 0; k < ARRAY_SIZE(cal_formats); k++) { - const struct cal_fmt *fmt = &cal_formats[k]; - - if (mbus_code.code == fmt->code) { - ctx->active_fmt[i] = fmt; - ctx_dbg(2, ctx, - "matched fourcc: %s: code: %04x idx: %u\n", - fourcc_to_str(fmt->fourcc), - fmt->code, i); - ctx->num_active_fmt = ++i; - } - } - } - - if (i == 0) { - ctx_err(ctx, "No suitable format reported by subdev %s\n", - subdev->name); - return -EINVAL; - } - - cal_ctx_v4l2_register(ctx); - - return 0; + return cal_ctx_v4l2_init_formats(ctx); } static int cal_async_complete(struct v4l2_async_notifier *notifier) { struct cal_ctx *ctx = notifier_to_ctx(notifier); - const struct cal_fmt *fmt; - struct v4l2_mbus_framefmt mbus_fmt; - int ret; - - ret = __subdev_get_format(ctx, &mbus_fmt); - if (ret) - return ret; - - fmt = find_format_by_code(ctx, mbus_fmt.code); - if (!fmt) { - ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n", - mbus_fmt.code); - return -EINVAL; - } - - /* Save current subdev format */ - v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); - ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; - cal_calc_format_size(ctx, fmt, &ctx->v_fmt); - ctx->fmt = fmt; - ctx->m_fmt = mbus_fmt; + cal_ctx_v4l2_register(ctx); return 0; } -- cgit v1.2.3 From 8fcb7576ad197b0d65fe68ef60b7e1097e18df0c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:22 +0200 Subject: media: ti-vpe: cal: Allow multiple contexts per subdev notifier The subdev notifier handling assumes a 1:1 mapping between CAL contexts and notifiers. To prepare for merging the multiple notifiers into a single one, retrieve the CAL context from the async subdev structure instead of from the notifier. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index b5eb6a640d70..83e790941e22 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2115,11 +2115,22 @@ static void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx) * ------------------------------------------------------------------ */ +struct cal_v4l2_async_subdev { + struct v4l2_async_subdev asd; + struct cal_ctx *ctx; +}; + +static inline struct cal_v4l2_async_subdev * +to_cal_asd(struct v4l2_async_subdev *asd) +{ + return container_of(asd, struct cal_v4l2_async_subdev, asd); +} + static int cal_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) { - struct cal_ctx *ctx = notifier_to_ctx(notifier); + struct cal_ctx *ctx = to_cal_asd(asd)->ctx; if (ctx->phy->sensor) { ctx_info(ctx, "Rejecting subdev %s (Already set!!)", @@ -2148,6 +2159,7 @@ static const struct v4l2_async_notifier_operations cal_async_ops = { static int of_cal_create_instance(struct cal_ctx *ctx, int inst) { + struct cal_v4l2_async_subdev *casd; struct v4l2_async_subdev *asd; struct fwnode_handle *fwnode; int ret; @@ -2163,6 +2175,9 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst) return PTR_ERR(asd); } + casd = to_cal_asd(asd); + casd->ctx = ctx; + ret = v4l2_async_notifier_register(&ctx->cal->v4l2_dev, &ctx->notifier); if (ret) { -- cgit v1.2.3 From a539a0536f6e22a26e3e85a4cfb87344f800016f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:23 +0200 Subject: media: ti-vpe: cal: Move async notifiers from contexts to cal_dev There's no need to create one async notifier per CAL context. Merge them all in a single notifier, stored in cal_dev. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 117 ++++++++++++++++++++++-------------- 1 file changed, 71 insertions(+), 46 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 83e790941e22..55528b4d749c 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -293,6 +293,7 @@ struct cal_dev { struct cal_ctx *ctx[CAL_NUM_CONTEXT]; struct v4l2_device v4l2_dev; + struct v4l2_async_notifier notifier; }; /* @@ -301,7 +302,6 @@ struct cal_dev { struct cal_ctx { struct v4l2_ctrl_handler ctrl_handler; struct video_device vdev; - struct v4l2_async_notifier notifier; struct cal_dev *cal; struct cal_camerarx *phy; @@ -337,11 +337,6 @@ struct cal_ctx { bool dma_act; }; -static inline struct cal_ctx *notifier_to_ctx(struct v4l2_async_notifier *n) -{ - return container_of(n, struct cal_ctx, notifier); -} - /* ------------------------------------------------------------------ * Platform Data * ------------------------------------------------------------------ @@ -2126,9 +2121,9 @@ to_cal_asd(struct v4l2_async_subdev *asd) return container_of(asd, struct cal_v4l2_async_subdev, asd); } -static int cal_async_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) +static int cal_async_notifier_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) { struct cal_ctx *ctx = to_cal_asd(asd)->ctx; @@ -2141,52 +2136,84 @@ static int cal_async_bound(struct v4l2_async_notifier *notifier, ctx->phy->sensor = subdev; ctx_dbg(1, ctx, "Using sensor %s for capture\n", subdev->name); - return cal_ctx_v4l2_init_formats(ctx); + return 0; } -static int cal_async_complete(struct v4l2_async_notifier *notifier) +static int cal_async_notifier_complete(struct v4l2_async_notifier *notifier) { - struct cal_ctx *ctx = notifier_to_ctx(notifier); + struct cal_dev *cal = container_of(notifier, struct cal_dev, notifier); + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { + struct cal_ctx *ctx = cal->ctx[i]; + + if (!ctx) + continue; + + ret = cal_ctx_v4l2_init_formats(ctx); + if (ret) + continue; + + cal_ctx_v4l2_register(ctx); + } - cal_ctx_v4l2_register(ctx); return 0; } -static const struct v4l2_async_notifier_operations cal_async_ops = { - .bound = cal_async_bound, - .complete = cal_async_complete, +static const struct v4l2_async_notifier_operations cal_async_notifier_ops = { + .bound = cal_async_notifier_bound, + .complete = cal_async_notifier_complete, }; -static int of_cal_create_instance(struct cal_ctx *ctx, int inst) +static int cal_async_notifier_register(struct cal_dev *cal) { - struct cal_v4l2_async_subdev *casd; - struct v4l2_async_subdev *asd; - struct fwnode_handle *fwnode; + unsigned int i; int ret; - v4l2_async_notifier_init(&ctx->notifier); - ctx->notifier.ops = &cal_async_ops; + v4l2_async_notifier_init(&cal->notifier); + cal->notifier.ops = &cal_async_notifier_ops; - fwnode = of_fwnode_handle(ctx->phy->sensor_node); - asd = v4l2_async_notifier_add_fwnode_subdev(&ctx->notifier, fwnode, - sizeof(*asd)); - if (IS_ERR(asd)) { - ctx_err(ctx, "Failed to add subdev to notifier\n"); - return PTR_ERR(asd); - } + for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { + struct cal_ctx *ctx = cal->ctx[i]; + struct cal_v4l2_async_subdev *casd; + struct v4l2_async_subdev *asd; + struct fwnode_handle *fwnode; + + if (!ctx) + continue; - casd = to_cal_asd(asd); - casd->ctx = ctx; + fwnode = of_fwnode_handle(ctx->phy->sensor_node); + asd = v4l2_async_notifier_add_fwnode_subdev(&cal->notifier, + fwnode, + sizeof(*asd)); + if (IS_ERR(asd)) { + ctx_err(ctx, "Failed to add subdev to notifier\n"); + ret = PTR_ERR(asd); + goto error; + } + + casd = to_cal_asd(asd); + casd->ctx = ctx; + } - ret = v4l2_async_notifier_register(&ctx->cal->v4l2_dev, - &ctx->notifier); + ret = v4l2_async_notifier_register(&cal->v4l2_dev, &cal->notifier); if (ret) { - ctx_err(ctx, "Error registering async notifier\n"); - v4l2_async_notifier_cleanup(&ctx->notifier); - return ret; + cal_err(cal, "Error registering async notifier\n"); + goto error; } return 0; + +error: + v4l2_async_notifier_cleanup(&cal->notifier); + return ret; +} + +static void cal_async_notifier_unregister(struct cal_dev *cal) +{ + v4l2_async_notifier_unregister(&cal->notifier); + v4l2_async_notifier_cleanup(&cal->notifier); } /* ------------------------------------------------------------------ @@ -2212,10 +2239,6 @@ static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) if (ret) return NULL; - ret = of_cal_create_instance(ctx, inst); - if (ret) - return NULL; - return ctx; } @@ -2356,6 +2379,11 @@ static int cal_probe(struct platform_device *pdev) cal_get_hwinfo(cal); pm_runtime_put_sync(&pdev->dev); + /* Register the async notifier. */ + ret = cal_async_notifier_register(cal); + if (ret) + goto error_pm_runtime; + return 0; error_pm_runtime: @@ -2366,11 +2394,8 @@ error_pm_runtime: error_context: for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { ctx = cal->ctx[i]; - if (ctx) { - v4l2_async_notifier_unregister(&ctx->notifier); - v4l2_async_notifier_cleanup(&ctx->notifier); + if (ctx) cal_ctx_v4l2_cleanup(ctx); - } } v4l2_device_unregister(&cal->v4l2_dev); @@ -2392,6 +2417,8 @@ static int cal_remove(struct platform_device *pdev) pm_runtime_get_sync(&pdev->dev); + cal_async_notifier_unregister(cal); + for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { ctx = cal->ctx[i]; if (ctx) { @@ -2399,8 +2426,6 @@ static int cal_remove(struct platform_device *pdev) video_device_node_name(&ctx->vdev)); cal_ctx_v4l2_unregister(ctx); cal_camerarx_disable(ctx->phy); - v4l2_async_notifier_unregister(&ctx->notifier); - v4l2_async_notifier_cleanup(&ctx->notifier); cal_ctx_v4l2_cleanup(ctx); } } -- cgit v1.2.3 From 539e7c5d7c028180ea16fc3b816cd07e0a09155d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:24 +0200 Subject: media: ti-vpe: cal: Replace context with phy in async notifier entries The async notifier is meant to bind to subdevs connected to the CSI-2 ports. Those ports are modelled by the CAMERARX phy instances. To prepare for additional decoupling of contexts and phys, make the notifier operate on phys. We still initialize and register the context V4L2 support in the async notifier complete operation as that's our signal that the userspace API is ready to be exposed. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 55528b4d749c..59fa1f8c1564 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2112,7 +2112,7 @@ static void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx) struct cal_v4l2_async_subdev { struct v4l2_async_subdev asd; - struct cal_ctx *ctx; + struct cal_camerarx *phy; }; static inline struct cal_v4l2_async_subdev * @@ -2125,16 +2125,16 @@ static int cal_async_notifier_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) { - struct cal_ctx *ctx = to_cal_asd(asd)->ctx; + struct cal_camerarx *phy = to_cal_asd(asd)->phy; - if (ctx->phy->sensor) { - ctx_info(ctx, "Rejecting subdev %s (Already set!!)", + if (phy->sensor) { + phy_info(phy, "Rejecting subdev %s (Already set!!)", subdev->name); return 0; } - ctx->phy->sensor = subdev; - ctx_dbg(1, ctx, "Using sensor %s for capture\n", subdev->name); + phy->sensor = subdev; + phy_dbg(1, phy, "Using sensor %s for capture\n", subdev->name); return 0; } @@ -2174,27 +2174,27 @@ static int cal_async_notifier_register(struct cal_dev *cal) v4l2_async_notifier_init(&cal->notifier); cal->notifier.ops = &cal_async_notifier_ops; - for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { - struct cal_ctx *ctx = cal->ctx[i]; + for (i = 0; i < ARRAY_SIZE(cal->phy); ++i) { + struct cal_camerarx *phy = cal->phy[i]; struct cal_v4l2_async_subdev *casd; struct v4l2_async_subdev *asd; struct fwnode_handle *fwnode; - if (!ctx) + if (!phy || !phy->sensor_node) continue; - fwnode = of_fwnode_handle(ctx->phy->sensor_node); + fwnode = of_fwnode_handle(phy->sensor_node); asd = v4l2_async_notifier_add_fwnode_subdev(&cal->notifier, fwnode, sizeof(*asd)); if (IS_ERR(asd)) { - ctx_err(ctx, "Failed to add subdev to notifier\n"); + phy_err(phy, "Failed to add subdev to notifier\n"); ret = PTR_ERR(asd); goto error; } casd = to_cal_asd(asd); - casd->ctx = ctx; + casd->phy = phy; } ret = v4l2_async_notifier_register(&cal->v4l2_dev, &cal->notifier); -- cgit v1.2.3 From 517cf52b7777894ce5c0e63f5b57f95b879b2bb5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:25 +0200 Subject: media: ti-vpe: cal: Operate on phy instances in cal_quickdump_regs() The cal_quickdump_regs() dumps registers for the two CAMERARX instances. Retrieve those instances from the cal_dev directly instead of going through the contexts, and simplify the code by using a loop. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 59fa1f8c1564..03b4d5351bfd 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -450,26 +450,24 @@ static inline void set_field(u32 *valp, u32 field, u32 mask) static void cal_quickdump_regs(struct cal_dev *cal) { + unsigned int i; + cal_info(cal, "CAL Registers @ 0x%pa:\n", &cal->res->start); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, (__force const void *)cal->base, resource_size(cal->res), false); - if (cal->ctx[0]) { - cal_info(cal, "CSI2 Core 0 Registers @ %pa:\n", - &cal->ctx[0]->phy->res->start); - print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)cal->ctx[0]->phy->base, - resource_size(cal->ctx[0]->phy->res), - false); - } + for (i = 0; i < ARRAY_SIZE(cal->phy); ++i) { + struct cal_camerarx *phy = cal->phy[i]; + + if (!phy) + continue; - if (cal->ctx[1]) { - cal_info(cal, "CSI2 Core 1 Registers @ %pa:\n", - &cal->ctx[1]->phy->res->start); + cal_info(cal, "CSI2 Core %u Registers @ %pa:\n", i, + &phy->res->start); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)cal->ctx[1]->phy->base, - resource_size(cal->ctx[1]->phy->res), + (__force const void *)phy->base, + resource_size(phy->res), false); } } -- cgit v1.2.3 From 497b5e94f820b3900dae89d34cd29db5ea394ff3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:26 +0200 Subject: media: ti-vpe: cal: Decouple context and phy cleanup at remove time The driver happens the use the same number of CAMERARX and context, but coupling their cleanup at remove time is wrong. To prepare for the introduction of additional contexts, decouple the two. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 03b4d5351bfd..e4d8cadf7bef 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1976,6 +1976,9 @@ static int cal_ctx_v4l2_register(struct cal_ctx *ctx) static void cal_ctx_v4l2_unregister(struct cal_ctx *ctx) { + ctx_dbg(1, ctx, "unregistering %s\n", + video_device_node_name(&ctx->vdev)); + video_unregister_device(&ctx->vdev); } @@ -2408,7 +2411,6 @@ error_camerarx: static int cal_remove(struct platform_device *pdev) { struct cal_dev *cal = platform_get_drvdata(pdev); - struct cal_ctx *ctx; unsigned int i; cal_dbg(1, cal, "Removing %s\n", CAL_MODULE_NAME); @@ -2418,14 +2420,18 @@ static int cal_remove(struct platform_device *pdev) cal_async_notifier_unregister(cal); for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { - ctx = cal->ctx[i]; - if (ctx) { - ctx_dbg(1, ctx, "unregistering %s\n", - video_device_node_name(&ctx->vdev)); - cal_ctx_v4l2_unregister(ctx); - cal_camerarx_disable(ctx->phy); - cal_ctx_v4l2_cleanup(ctx); - } + if (cal->ctx[i]) + cal_ctx_v4l2_unregister(cal->ctx[i]); + } + + for (i = 0; i < ARRAY_SIZE(cal->phy); i++) { + if (cal->phy[i]) + cal_camerarx_disable(cal->phy[i]); + } + + for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { + if (cal->ctx[i]) + cal_ctx_v4l2_cleanup(cal->ctx[i]); } v4l2_device_unregister(&cal->v4l2_dev); -- cgit v1.2.3 From 4c0ed7ba65d1c2e9476c083ba83c23fe68ef6ebc Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:27 +0200 Subject: media: ti-vpe: cal: Move CAL_NUM_CSI2_PORTS from cal_regs.h to cal.c The CAL_NUM_CSI2_PORTS macro isn't a register definition. Move it to cal.c, and fix indentation of the other macros while at it. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 7 ++++--- drivers/media/platform/ti-vpe/cal_regs.h | 2 -- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index e4d8cadf7bef..67b87d33639d 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -75,10 +75,11 @@ MODULE_PARM_DESC(debug, "activates debug info"); #define phy_err(phy, fmt, arg...) \ cal_err((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) -#define CAL_NUM_CONTEXT 2 +#define CAL_NUM_CONTEXT 2 +#define CAL_NUM_CSI2_PORTS 2 -#define MAX_WIDTH_BYTES (8192 * 8) -#define MAX_HEIGHT_LINES 16383 +#define MAX_WIDTH_BYTES (8192 * 8) +#define MAX_HEIGHT_LINES 16383 /* ------------------------------------------------------------------ * Format Handling diff --git a/drivers/media/platform/ti-vpe/cal_regs.h b/drivers/media/platform/ti-vpe/cal_regs.h index b551e67d5d41..f752096dcf7f 100644 --- a/drivers/media/platform/ti-vpe/cal_regs.h +++ b/drivers/media/platform/ti-vpe/cal_regs.h @@ -34,8 +34,6 @@ */ #define DRA72_CAL_PRE_ES2_LDO_DISABLE BIT(0) -#define CAL_NUM_CSI2_PORTS 2 - /* CAL register offsets */ #define CAL_HL_REVISION 0x0000 -- cgit v1.2.3 From 7e75b1502f65d70088f522cb4474502644b44428 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:28 +0200 Subject: media: ti-vpe: cal: Remove isvcirqset() and isportirqset() macros The isvcirqset() isn't used. The isportirqset() doesn't increase readability. Inline the latter and simply drop the former. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 67b87d33639d..e57ee6010555 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1265,11 +1265,6 @@ static inline void cal_process_buffer_complete(struct cal_ctx *ctx) ctx->cur_frm = ctx->next_frm; } -#define isvcirqset(irq, vc, ff) (irq & \ - (CAL_CSI2_VC_IRQENABLE_ ##ff ##_IRQ_##vc ##_MASK)) - -#define isportirqset(irq, port) (irq & CAL_HL_IRQ_MASK(port)) - static irqreturn_t cal_irq(int irq_cal, void *data) { struct cal_dev *cal = data; @@ -1309,7 +1304,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) reg_write(cal, CAL_HL_IRQSTATUS(1), status); for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { - if (isportirqset(status, i)) { + if (status & CAL_HL_IRQ_MASK(i)) { ctx = cal->ctx[i]; spin_lock(&ctx->slock); @@ -1332,7 +1327,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) reg_write(cal, CAL_HL_IRQSTATUS(2), status); for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { - if (isportirqset(status, i)) { + if (status & CAL_HL_IRQ_MASK(i)) { ctx = cal->ctx[i]; dma_q = &ctx->vidq; -- cgit v1.2.3 From a0db060fb100c16b533595f468ed57ba4bb2d5a3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:29 +0200 Subject: media: ti-vpe: cal: Replace number of ports numerical value by macro Use the CAL_NUM_CSI2_PORTS macro instead of the hardcoded numerical value 2 to iterate over the CSI-2 ports. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index e57ee6010555..617b17133071 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1281,7 +1281,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) if (status & CAL_HL_IRQ_OCPO_ERR_MASK) dev_err_ratelimited(&cal->pdev->dev, "OCPO ERROR\n"); - for (i = 0; i < 2; ++i) { + for (i = 0; i < CAL_NUM_CSI2_PORTS; ++i) { if (status & CAL_HL_IRQ_CIO_MASK(i)) { u32 cio_stat = reg_read(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); -- cgit v1.2.3 From 45dd15e6ce2241da7d3f08b86b680d8b7d3dcd67 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:30 +0200 Subject: media: ti-vpe: cal: Split media initialization and cleanup to functions Create four functions to handle initialization, cleanup, registration and unregistration of the V4L2 (and soon media controller) objects: - init() is meant to be called early at probe time to initialize the objects, before they get used from within the kernel - cleanup() is the counterpart of init, and is meant to be called at the end of the remove sequence to free all objects - register() is meant to be called at the end of the probe sequence, to register the userspace-facing devices - unregister() is the counterpart of register, and is meant to be called at the beginning for the remove sequence, to disallow access from userspace Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 116 ++++++++++++++++++++++++++++-------- 1 file changed, 90 insertions(+), 26 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 617b17133071..340cbf385d42 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2213,6 +2213,88 @@ static void cal_async_notifier_unregister(struct cal_dev *cal) v4l2_async_notifier_cleanup(&cal->notifier); } +/* ------------------------------------------------------------------ + * Media and V4L2 device handling + * ------------------------------------------------------------------ + */ + +/* + * Register user-facing devices. To be called at the end of the probe function + * when all resources are initialized and ready. + */ +static int cal_media_register(struct cal_dev *cal) +{ + int ret; + + /* + * Register the async notifier. This may trigger registration of the + * V4L2 video devices if all subdevs are ready. + */ + ret = cal_async_notifier_register(cal); + if (ret) + return ret; + + return 0; +} + +/* + * Unregister the user-facing devices, but don't free memory yet. To be called + * at the beginning of the remove function, to disallow access from userspace. + */ +static void cal_media_unregister(struct cal_dev *cal) +{ + unsigned int i; + + /* Unregister all the V4L2 video devices. */ + for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { + if (cal->ctx[i]) + cal_ctx_v4l2_unregister(cal->ctx[i]); + } + + cal_async_notifier_unregister(cal); +} + +/* + * Initialize the in-kernel objects. To be called at the beginning of the probe + * function, before the V4L2 device is used by the driver. + */ +static int cal_media_init(struct cal_dev *cal) +{ + int ret; + + /* + * Initialize the V4L2 device (despite the function name, this performs + * initialization, not registration). + */ + ret = v4l2_device_register(&cal->pdev->dev, &cal->v4l2_dev); + if (ret) { + cal_err(cal, "Failed to register V4L2 device\n"); + return ret; + } + + vb2_dma_contig_set_max_seg_size(&cal->pdev->dev, DMA_BIT_MASK(32)); + + return 0; +} + +/* + * Cleanup the in-kernel objects, freeing memory. To be called at the very end + * of the remove sequence, when nothing (including userspace) can access the + * objects anymore. + */ +static void cal_media_cleanup(struct cal_dev *cal) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { + if (cal->ctx[i]) + cal_ctx_v4l2_cleanup(cal->ctx[i]); + } + + v4l2_device_unregister(&cal->v4l2_dev); + vb2_dma_contig_clear_max_seg_size(&cal->pdev->dev); +} + /* ------------------------------------------------------------------ * Initialization and module stuff * ------------------------------------------------------------------ @@ -2345,12 +2427,10 @@ static int cal_probe(struct platform_device *pdev) goto error_camerarx; } - /* Register the V4L2 device. */ - ret = v4l2_device_register(&pdev->dev, &cal->v4l2_dev); - if (ret) { - cal_err(cal, "Failed to register V4L2 device\n"); + /* Initialize the media device. */ + ret = cal_media_init(cal); + if (ret < 0) goto error_camerarx; - } /* Create contexts. */ for (i = 0; i < cal->data->num_csi2_phy; ++i) { @@ -2365,8 +2445,6 @@ static int cal_probe(struct platform_device *pdev) } } - vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); - /* Read the revision and hardware info to verify hardware access. */ pm_runtime_enable(&pdev->dev); ret = pm_runtime_get_sync(&pdev->dev); @@ -2376,16 +2454,14 @@ static int cal_probe(struct platform_device *pdev) cal_get_hwinfo(cal); pm_runtime_put_sync(&pdev->dev); - /* Register the async notifier. */ - ret = cal_async_notifier_register(cal); + /* Register the media device. */ + ret = cal_media_register(cal); if (ret) goto error_pm_runtime; return 0; error_pm_runtime: - vb2_dma_contig_clear_max_seg_size(&pdev->dev); - pm_runtime_disable(&pdev->dev); error_context: @@ -2395,7 +2471,7 @@ error_context: cal_ctx_v4l2_cleanup(ctx); } - v4l2_device_unregister(&cal->v4l2_dev); + cal_media_cleanup(cal); error_camerarx: for (i = 0; i < ARRAY_SIZE(cal->phy); i++) @@ -2413,24 +2489,14 @@ static int cal_remove(struct platform_device *pdev) pm_runtime_get_sync(&pdev->dev); - cal_async_notifier_unregister(cal); - - for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { - if (cal->ctx[i]) - cal_ctx_v4l2_unregister(cal->ctx[i]); - } + cal_media_unregister(cal); for (i = 0; i < ARRAY_SIZE(cal->phy); i++) { if (cal->phy[i]) cal_camerarx_disable(cal->phy[i]); } - for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { - if (cal->ctx[i]) - cal_ctx_v4l2_cleanup(cal->ctx[i]); - } - - v4l2_device_unregister(&cal->v4l2_dev); + cal_media_cleanup(cal); for (i = 0; i < ARRAY_SIZE(cal->phy); i++) cal_camerarx_destroy(cal->phy[i]); @@ -2438,8 +2504,6 @@ static int cal_remove(struct platform_device *pdev) pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - vb2_dma_contig_clear_max_seg_size(&pdev->dev); - return 0; } -- cgit v1.2.3 From 05930cf1e5c5d7d526e492cfe0f0bb3116bc29de Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:31 +0200 Subject: media: ti-vpe: cal: Read hardware revision earlier during probe Read the hardware revision and info right after allocating resources, as there's no need to delay doing so until all initialization is complete. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 340cbf385d42..fca591a94aca 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2408,6 +2408,15 @@ static int cal_probe(struct platform_device *pdev) if (ret) return ret; + /* Read the revision and hardware info to verify hardware access. */ + pm_runtime_enable(&pdev->dev); + ret = pm_runtime_get_sync(&pdev->dev); + if (ret) + goto error_pm_runtime; + + cal_get_hwinfo(cal); + pm_runtime_put_sync(&pdev->dev); + /* Create CAMERARX PHYs. */ for (i = 0; i < cal->data->num_csi2_phy; ++i) { cal->phy[i] = cal_camerarx_create(cal, i); @@ -2445,25 +2454,13 @@ static int cal_probe(struct platform_device *pdev) } } - /* Read the revision and hardware info to verify hardware access. */ - pm_runtime_enable(&pdev->dev); - ret = pm_runtime_get_sync(&pdev->dev); - if (ret) - goto error_pm_runtime; - - cal_get_hwinfo(cal); - pm_runtime_put_sync(&pdev->dev); - /* Register the media device. */ ret = cal_media_register(cal); if (ret) - goto error_pm_runtime; + goto error_context; return 0; -error_pm_runtime: - pm_runtime_disable(&pdev->dev); - error_context: for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { ctx = cal->ctx[i]; @@ -2477,6 +2474,9 @@ error_camerarx: for (i = 0; i < ARRAY_SIZE(cal->phy); i++) cal_camerarx_destroy(cal->phy[i]); +error_pm_runtime: + pm_runtime_disable(&pdev->dev); + return ret; } -- cgit v1.2.3 From dfbb172e512eaba56039e0b4c485256295442147 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:32 +0200 Subject: media: ti-vpe: cal: Print revision and hwinfo in a more readable format Print the hardware revision in the X.Y.R format, which is more readable that the 32-bit hex value. For the hardware info register, only print its value if it doesn't contain what we expect. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index fca591a94aca..ec52cb7f3039 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2342,21 +2342,36 @@ static const struct of_device_id cal_of_match[] = { }; MODULE_DEVICE_TABLE(of, cal_of_match); -/* - * Get Revision and HW info - */ +/* Get hardware revision and info. */ + +#define CAL_HL_HWINFO_VALUE 0xa3c90469 + static void cal_get_hwinfo(struct cal_dev *cal) { u32 revision; u32 hwinfo; revision = reg_read(cal, CAL_HL_REVISION); - cal_dbg(3, cal, "CAL_HL_REVISION = 0x%08x (expecting 0x40000200)\n", - revision); + switch (FIELD_GET(CAL_HL_REVISION_SCHEME_MASK, revision)) { + case CAL_HL_REVISION_SCHEME_H08: + cal_dbg(3, cal, "CAL HW revision %lu.%lu.%lu (0x%08x)\n", + FIELD_GET(CAL_HL_REVISION_MAJOR_MASK, revision), + FIELD_GET(CAL_HL_REVISION_MINOR_MASK, revision), + FIELD_GET(CAL_HL_REVISION_RTL_MASK, revision), + revision); + break; + + case CAL_HL_REVISION_SCHEME_LEGACY: + default: + cal_info(cal, "Unexpected CAL HW revision 0x%08x\n", + revision); + break; + } hwinfo = reg_read(cal, CAL_HL_HWINFO); - cal_dbg(3, cal, "CAL_HL_HWINFO = 0x%08x (expecting 0xA3C90469)\n", - hwinfo); + if (hwinfo != CAL_HL_HWINFO_VALUE) + cal_info(cal, "CAL_HL_HWINFO = 0x%08x, expected 0x%08x\n", + hwinfo, CAL_HL_HWINFO_VALUE); } static int cal_probe(struct platform_device *pdev) -- cgit v1.2.3 From 92790656cee2f0b02855452d73f85127ac68081c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:33 +0200 Subject: media: ti-vpe: cal: Store struct device in cal_dev The cal_dev structure stores the platform_device pointer, but most accesses to that field need the device pointer. Store the struct device pointer directly to simplify the code, and use to_platform_device() in the two locations that need the platform device. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 55 ++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 28 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index ec52cb7f3039..42b948e784d2 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -53,13 +53,12 @@ MODULE_PARM_DESC(debug, "activates debug info"); #define cal_dbg(level, cal, fmt, arg...) \ do { \ if (debug >= (level)) \ - dev_printk(KERN_DEBUG, &(cal)->pdev->dev, \ - fmt, ##arg); \ + dev_printk(KERN_DEBUG, (cal)->dev, fmt, ##arg); \ } while (0) #define cal_info(cal, fmt, arg...) \ - dev_info(&(cal)->pdev->dev, fmt, ##arg) + dev_info((cal)->dev, fmt, ##arg) #define cal_err(cal, fmt, arg...) \ - dev_err(&(cal)->pdev->dev, fmt, ##arg) + dev_err((cal)->dev, fmt, ##arg) #define ctx_dbg(level, ctx, fmt, arg...) \ cal_dbg(level, (ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) @@ -263,7 +262,7 @@ struct cal_data { struct cal_camerarx { void __iomem *base; struct resource *res; - struct platform_device *pdev; + struct device *dev; struct regmap_field *fields[F_MAX_FIELDS]; struct cal_dev *cal; @@ -280,7 +279,7 @@ struct cal_dev { int irq; void __iomem *base; struct resource *res; - struct platform_device *pdev; + struct device *dev; const struct cal_data *data; @@ -908,7 +907,7 @@ static int cal_camerarx_regmap_init(struct cal_dev *cal, * Here we update the reg offset with the * value found in DT */ - phy->fields[i] = devm_regmap_field_alloc(&cal->pdev->dev, + phy->fields[i] = devm_regmap_field_alloc(cal->dev, cal->syscon_camerrx, field); if (IS_ERR(phy->fields[i])) { @@ -923,7 +922,6 @@ static int cal_camerarx_regmap_init(struct cal_dev *cal, static int cal_camerarx_parse_dt(struct cal_camerarx *phy) { struct v4l2_fwnode_endpoint *endpoint = &phy->endpoint; - struct platform_device *pdev = phy->cal->pdev; struct device_node *ep_node; char data_lanes[V4L2_FWNODE_CSI2_MAX_DATA_LANES * 2]; unsigned int i; @@ -933,7 +931,7 @@ static int cal_camerarx_parse_dt(struct cal_camerarx *phy) * Find the endpoint node for the port corresponding to the PHY * instance, and parse its CSI-2-related properties. */ - ep_node = of_graph_get_endpoint_by_regs(pdev->dev.of_node, + ep_node = of_graph_get_endpoint_by_regs(phy->cal->dev->of_node, phy->instance, 0); if (!ep_node) { /* @@ -990,7 +988,7 @@ done: static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, unsigned int instance) { - struct platform_device *pdev = cal->pdev; + struct platform_device *pdev = to_platform_device(cal->dev); struct cal_camerarx *phy; int ret; @@ -1006,7 +1004,7 @@ static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, (instance == 0) ? "cal_rx_core0" : "cal_rx_core1"); - phy->base = devm_ioremap_resource(&pdev->dev, phy->res); + phy->base = devm_ioremap_resource(cal->dev, phy->res); if (IS_ERR(phy->base)) { cal_err(cal, "failed to ioremap\n"); ret = PTR_ERR(phy->base); @@ -1042,7 +1040,8 @@ static void cal_camerarx_destroy(struct cal_camerarx *phy) static int cal_camerarx_init_regmap(struct cal_dev *cal) { - struct device_node *np = cal->pdev->dev.of_node; + struct platform_device *pdev = to_platform_device(cal->dev); + struct device_node *np = cal->dev->of_node; struct regmap_config config = { }; struct regmap *syscon; struct resource *res; @@ -1057,16 +1056,16 @@ static int cal_camerarx_init_regmap(struct cal_dev *cal) return 0; } - dev_warn(&cal->pdev->dev, "failed to get ti,camerrx-control: %ld\n", + dev_warn(cal->dev, "failed to get ti,camerrx-control: %ld\n", PTR_ERR(syscon)); /* * Backward DTS compatibility. If syscon entry is not present then * check if the camerrx_control resource is present. */ - res = platform_get_resource_byname(cal->pdev, IORESOURCE_MEM, + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "camerrx_control"); - base = devm_ioremap_resource(&cal->pdev->dev, res); + base = devm_ioremap_resource(cal->dev, res); if (IS_ERR(base)) { cal_err(cal, "failed to ioremap camerrx_control\n"); return PTR_ERR(base); @@ -1157,7 +1156,7 @@ static void cal_ctx_pix_proc_config(struct cal_ctx *ctx) * * Instead of failing here just use 8 bpp as a default. */ - dev_warn_once(&ctx->cal->pdev->dev, + dev_warn_once(ctx->cal->dev, "%s:%d:%s: bpp:%d unsupported! Overwritten with 8.\n", __FILE__, __LINE__, __func__, ctx->fmt->bpp); extract = CAL_PIX_PROC_EXTRACT_B8; @@ -1279,14 +1278,14 @@ static irqreturn_t cal_irq(int irq_cal, void *data) reg_write(cal, CAL_HL_IRQSTATUS(0), status); if (status & CAL_HL_IRQ_OCPO_ERR_MASK) - dev_err_ratelimited(&cal->pdev->dev, "OCPO ERROR\n"); + dev_err_ratelimited(cal->dev, "OCPO ERROR\n"); for (i = 0; i < CAL_NUM_CSI2_PORTS; ++i) { if (status & CAL_HL_IRQ_CIO_MASK(i)) { u32 cio_stat = reg_read(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); - dev_err_ratelimited(&cal->pdev->dev, + dev_err_ratelimited(cal->dev, "CIO%u error: %#08x\n", i, cio_stat); reg_write(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i), @@ -1388,7 +1387,7 @@ static int cal_querycap(struct file *file, void *priv, strscpy(cap->card, CAL_MODULE_NAME, sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), - "platform:%s", dev_name(&ctx->cal->pdev->dev)); + "platform:%s", dev_name(ctx->cal->dev)); return 0; } @@ -1822,7 +1821,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) goto err; } - pm_runtime_get_sync(&ctx->cal->pdev->dev); + pm_runtime_get_sync(ctx->cal->dev); cal_ctx_csi2_config(ctx); cal_ctx_pix_proc_config(ctx); @@ -1837,7 +1836,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) if (ret) { v4l2_subdev_call(ctx->phy->sensor, core, s_power, 0); ctx_err(ctx, "stream on failed in subdev\n"); - pm_runtime_put_sync(&ctx->cal->pdev->dev); + pm_runtime_put_sync(ctx->cal->dev); goto err; } @@ -1917,7 +1916,7 @@ static void cal_stop_streaming(struct vb2_queue *vq) ctx->next_frm = NULL; spin_unlock_irqrestore(&ctx->slock, flags); - pm_runtime_put_sync(&ctx->cal->pdev->dev); + pm_runtime_put_sync(ctx->cal->dev); } static const struct vb2_ops cal_video_qops = { @@ -2065,7 +2064,7 @@ static int cal_ctx_v4l2_init(struct cal_ctx *ctx) q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &ctx->mutex; q->min_buffers_needed = 3; - q->dev = &ctx->cal->pdev->dev; + q->dev = ctx->cal->dev; ret = vb2_queue_init(q); if (ret) @@ -2266,13 +2265,13 @@ static int cal_media_init(struct cal_dev *cal) * Initialize the V4L2 device (despite the function name, this performs * initialization, not registration). */ - ret = v4l2_device_register(&cal->pdev->dev, &cal->v4l2_dev); + ret = v4l2_device_register(cal->dev, &cal->v4l2_dev); if (ret) { cal_err(cal, "Failed to register V4L2 device\n"); return ret; } - vb2_dma_contig_set_max_seg_size(&cal->pdev->dev, DMA_BIT_MASK(32)); + vb2_dma_contig_set_max_seg_size(cal->dev, DMA_BIT_MASK(32)); return 0; } @@ -2292,7 +2291,7 @@ static void cal_media_cleanup(struct cal_dev *cal) } v4l2_device_unregister(&cal->v4l2_dev); - vb2_dma_contig_clear_max_seg_size(&cal->pdev->dev); + vb2_dma_contig_clear_max_seg_size(cal->dev); } /* ------------------------------------------------------------------ @@ -2305,7 +2304,7 @@ static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) struct cal_ctx *ctx; int ret; - ctx = devm_kzalloc(&cal->pdev->dev, sizeof(*ctx), GFP_KERNEL); + ctx = devm_kzalloc(cal->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return NULL; @@ -2393,7 +2392,7 @@ static int cal_probe(struct platform_device *pdev) return -ENODEV; } - cal->pdev = pdev; + cal->dev = &pdev->dev; platform_set_drvdata(pdev, cal); /* Acquire resources: clocks, CAMERARX regmap, I/O memory and IRQ. */ -- cgit v1.2.3 From b908235114d122ad59c8287f2dad0a1bd45b4e3a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:34 +0200 Subject: media: ti-vpe: cal: Register a media device Enable the media controller API by registering a media device and initializing the media entities corresponding to the video devices. The context initialization is slightly refactored as a result. The media graph will be built in a subsequent change. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 71 +++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 22 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 42b948e784d2..236fe826b635 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -282,6 +283,7 @@ struct cal_dev { struct device *dev; const struct cal_data *data; + u32 revision; /* Control Module handle */ struct regmap *syscon_camerrx; @@ -292,6 +294,7 @@ struct cal_dev { struct cal_ctx *ctx[CAL_NUM_CONTEXT]; + struct media_device mdev; struct v4l2_device v4l2_dev; struct v4l2_async_notifier notifier; }; @@ -302,6 +305,7 @@ struct cal_dev { struct cal_ctx { struct v4l2_ctrl_handler ctrl_handler; struct video_device vdev; + struct media_pad pad; struct cal_dev *cal; struct cal_camerarx *phy; @@ -2050,11 +2054,11 @@ static int cal_ctx_v4l2_init(struct cal_ctx *ctx) struct vb2_queue *q = &ctx->vb_vidq; int ret; - /* initialize locks */ + INIT_LIST_HEAD(&ctx->vidq.active); spin_lock_init(&ctx->slock); mutex_init(&ctx->mutex); - /* initialize queue */ + /* Initialize the vb2 queue. */ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; q->drv_priv = ctx; @@ -2070,35 +2074,39 @@ static int cal_ctx_v4l2_init(struct cal_ctx *ctx) if (ret) return ret; - /* init video dma queues */ - INIT_LIST_HEAD(&ctx->vidq.active); - + /* Initialize the video device and media entity. */ *vfd = cal_videodev; vfd->v4l2_dev = &ctx->cal->v4l2_dev; vfd->queue = q; + snprintf(vfd->name, sizeof(vfd->name), "CAL output %u", ctx->index); + vfd->lock = &ctx->mutex; + video_set_drvdata(vfd, ctx); + + ctx->pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&vfd->entity, 1, &ctx->pad); + if (ret < 0) + return ret; /* Initialize the control handler. */ ret = v4l2_ctrl_handler_init(hdl, 11); if (ret < 0) { ctx_err(ctx, "Failed to init ctrl handler\n"); - return ret; + goto error; } vfd->ctrl_handler = hdl; - /* - * Provide a mutex to v4l2 core. It will be used to protect - * all fops and v4l2 ioctls. - */ - vfd->lock = &ctx->mutex; - video_set_drvdata(vfd, ctx); - return 0; + +error: + media_entity_cleanup(&vfd->entity); + return ret; } static void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx) { v4l2_ctrl_handler_free(&ctx->ctrl_handler); + media_entity_cleanup(&ctx->vdev.entity); } /* ------------------------------------------------------------------ @@ -2225,13 +2233,21 @@ static int cal_media_register(struct cal_dev *cal) { int ret; + ret = media_device_register(&cal->mdev); + if (ret) { + cal_err(cal, "Failed to register media device\n"); + return ret; + } + /* * Register the async notifier. This may trigger registration of the * V4L2 video devices if all subdevs are ready. */ ret = cal_async_notifier_register(cal); - if (ret) + if (ret) { + media_device_unregister(&cal->mdev); return ret; + } return 0; } @@ -2251,6 +2267,7 @@ static void cal_media_unregister(struct cal_dev *cal) } cal_async_notifier_unregister(cal); + media_device_unregister(&cal->mdev); } /* @@ -2259,12 +2276,21 @@ static void cal_media_unregister(struct cal_dev *cal) */ static int cal_media_init(struct cal_dev *cal) { + struct media_device *mdev = &cal->mdev; int ret; + mdev->dev = cal->dev; + mdev->hw_revision = cal->revision; + strscpy(mdev->model, "CAL", sizeof(mdev->model)); + snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s", + dev_name(mdev->dev)); + media_device_init(mdev); + /* * Initialize the V4L2 device (despite the function name, this performs * initialization, not registration). */ + cal->v4l2_dev.mdev = mdev; ret = v4l2_device_register(cal->dev, &cal->v4l2_dev); if (ret) { cal_err(cal, "Failed to register V4L2 device\n"); @@ -2291,6 +2317,8 @@ static void cal_media_cleanup(struct cal_dev *cal) } v4l2_device_unregister(&cal->v4l2_dev); + media_device_cleanup(&cal->mdev); + vb2_dma_contig_clear_max_seg_size(cal->dev); } @@ -2347,23 +2375,22 @@ MODULE_DEVICE_TABLE(of, cal_of_match); static void cal_get_hwinfo(struct cal_dev *cal) { - u32 revision; u32 hwinfo; - revision = reg_read(cal, CAL_HL_REVISION); - switch (FIELD_GET(CAL_HL_REVISION_SCHEME_MASK, revision)) { + cal->revision = reg_read(cal, CAL_HL_REVISION); + switch (FIELD_GET(CAL_HL_REVISION_SCHEME_MASK, cal->revision)) { case CAL_HL_REVISION_SCHEME_H08: cal_dbg(3, cal, "CAL HW revision %lu.%lu.%lu (0x%08x)\n", - FIELD_GET(CAL_HL_REVISION_MAJOR_MASK, revision), - FIELD_GET(CAL_HL_REVISION_MINOR_MASK, revision), - FIELD_GET(CAL_HL_REVISION_RTL_MASK, revision), - revision); + FIELD_GET(CAL_HL_REVISION_MAJOR_MASK, cal->revision), + FIELD_GET(CAL_HL_REVISION_MINOR_MASK, cal->revision), + FIELD_GET(CAL_HL_REVISION_RTL_MASK, cal->revision), + cal->revision); break; case CAL_HL_REVISION_SCHEME_LEGACY: default: cal_info(cal, "Unexpected CAL HW revision 0x%08x\n", - revision); + cal->revision); break; } -- cgit v1.2.3 From 1b22b7a0ec9c505ac9b55adeb84a7c15cbd0bbb0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:35 +0200 Subject: media: ti-vpe: cal: Init formats in cal_ctx_v4l2_register() To prepare for splitting the V4L2 API support to a separate file, call cal_ctx_v4l2_init_formats() from cal_ctx_v4l2_register(). Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 83 +++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 44 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 236fe826b635..26747e6da358 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1948,39 +1948,6 @@ static const struct video_device cal_videodev = { V4L2_CAP_READWRITE, }; -static int cal_ctx_v4l2_register(struct cal_ctx *ctx) -{ - struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; - struct video_device *vfd = &ctx->vdev; - int ret; - - ret = v4l2_ctrl_add_handler(hdl, ctx->phy->sensor->ctrl_handler, NULL, - true); - if (ret < 0) { - ctx_err(ctx, "Failed to add sensor ctrl handler\n"); - return ret; - } - - ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr); - if (ret < 0) { - ctx_err(ctx, "Failed to register video device\n"); - return ret; - } - - ctx_info(ctx, "V4L2 device registered as %s\n", - video_device_node_name(vfd)); - - return 0; -} - -static void cal_ctx_v4l2_unregister(struct cal_ctx *ctx) -{ - ctx_dbg(1, ctx, "unregistering %s\n", - video_device_node_name(&ctx->vdev)); - - video_unregister_device(&ctx->vdev); -} - static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) { struct v4l2_subdev_mbus_code_enum mbus_code; @@ -2047,6 +2014,43 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) return 0; } +static int cal_ctx_v4l2_register(struct cal_ctx *ctx) +{ + struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; + struct video_device *vfd = &ctx->vdev; + int ret; + + ret = cal_ctx_v4l2_init_formats(ctx); + if (ret) + return ret; + + ret = v4l2_ctrl_add_handler(hdl, ctx->phy->sensor->ctrl_handler, NULL, + true); + if (ret < 0) { + ctx_err(ctx, "Failed to add sensor ctrl handler\n"); + return ret; + } + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr); + if (ret < 0) { + ctx_err(ctx, "Failed to register video device\n"); + return ret; + } + + ctx_info(ctx, "V4L2 device registered as %s\n", + video_device_node_name(vfd)); + + return 0; +} + +static void cal_ctx_v4l2_unregister(struct cal_ctx *ctx) +{ + ctx_dbg(1, ctx, "unregistering %s\n", + video_device_node_name(&ctx->vdev)); + + video_unregister_device(&ctx->vdev); +} + static int cal_ctx_v4l2_init(struct cal_ctx *ctx) { struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; @@ -2147,19 +2151,10 @@ static int cal_async_notifier_complete(struct v4l2_async_notifier *notifier) { struct cal_dev *cal = container_of(notifier, struct cal_dev, notifier); unsigned int i; - int ret; for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { - struct cal_ctx *ctx = cal->ctx[i]; - - if (!ctx) - continue; - - ret = cal_ctx_v4l2_init_formats(ctx); - if (ret) - continue; - - cal_ctx_v4l2_register(ctx); + if (cal->ctx[i]) + cal_ctx_v4l2_register(cal->ctx[i]); } return 0; -- cgit v1.2.3 From 0a8e64ed2100511454d652d03f5ec9098ded5c44 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:36 +0200 Subject: media: ti-vpe: cal: Allocate cal_ctx active_fmt array dynamically To avoid making the cal_ctx structure layoug depend on the size of the cal_formats array, allocate the active_fmt array dynamically. This prepares for splitting the driver in multiple files. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 26747e6da358..31878a32f6a1 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -325,7 +325,7 @@ struct cal_ctx { struct v4l2_mbus_framefmt m_fmt; /* Current subdev enumerated format */ - const struct cal_fmt *active_fmt[ARRAY_SIZE(cal_formats)]; + const struct cal_fmt **active_fmt; unsigned int num_active_fmt; unsigned int sequence; @@ -1957,7 +1957,13 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) int ret = 0; /* Enumerate sub device formats and enable all matching local formats */ + ctx->active_fmt = devm_kcalloc(ctx->cal->dev, ARRAY_SIZE(cal_formats), + sizeof(*ctx->active_fmt), GFP_KERNEL); + if (!ctx->active_fmt) + return -ENOMEM; + ctx->num_active_fmt = 0; + for (j = 0, i = 0; ret != -EINVAL; ++j) { memset(&mbus_code, 0, sizeof(mbus_code)); -- cgit v1.2.3 From cd2144603ad3276cb0379f5f70d85cedcc68ecb4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:37 +0200 Subject: media: ti-vpe: cal: Inline cal_camerarx_max_lanes() in its only caller The cal_camerarx_max_lanes() function is a one-liner that has a single caller. It doesn't improve readability. Inline it in its caller. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 31878a32f6a1..4be1fa7e8197 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -481,19 +481,13 @@ static void cal_quickdump_regs(struct cal_dev *cal) * ------------------------------------------------------------------ */ -static u32 cal_camerarx_max_lanes(struct cal_camerarx *phy) -{ - return phy->cal->data->camerarx[phy->instance].num_lanes; -} - static void cal_camerarx_enable(struct cal_camerarx *phy) { - u32 max_lanes; + u32 num_lanes = phy->cal->data->camerarx[phy->instance].num_lanes; regmap_field_write(phy->fields[F_CAMMODE], 0); /* Always enable all lanes at the phy control level */ - max_lanes = (1 << cal_camerarx_max_lanes(phy)) - 1; - regmap_field_write(phy->fields[F_LANEENABLE], max_lanes); + regmap_field_write(phy->fields[F_LANEENABLE], (1 << num_lanes) - 1); /* F_CSI_MODE is not present on every architecture */ if (phy->fields[F_CSI_MODE]) regmap_field_write(phy->fields[F_CSI_MODE], 1); -- cgit v1.2.3 From e085ede10d690f25e21e47d248bc4ab2bfd5f7bf Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:38 +0200 Subject: media: ti-vpe: cal: Reorder camerarx functions to prepare refactoring To prepare for the camerarx refactoring, reorder functions without any functional change to ease review of the refactoring itself. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 428 ++++++++++++++++++------------------ 1 file changed, 214 insertions(+), 214 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 4be1fa7e8197..6b0ed2946eba 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -481,6 +481,54 @@ static void cal_quickdump_regs(struct cal_dev *cal) * ------------------------------------------------------------------ */ +static int cal_camerarx_get_external_info(struct cal_camerarx *phy) +{ + struct v4l2_ctrl *ctrl; + + if (!phy->sensor) + return -ENODEV; + + ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); + if (!ctrl) { + phy_err(phy, "no pixel rate control in subdev: %s\n", + phy->sensor->name); + return -EPIPE; + } + + phy->external_rate = v4l2_ctrl_g_ctrl_int64(ctrl); + phy_dbg(3, phy, "sensor Pixel Rate: %u\n", phy->external_rate); + + return 0; +} + +static void cal_camerarx_lane_config(struct cal_camerarx *phy) +{ + u32 val = reg_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)); + u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; + u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; + struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = + &phy->endpoint.bus.mipi_csi2; + int lane; + + set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); + set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask); + for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) { + /* + * Every lane are one nibble apart starting with the + * clock followed by the data lanes so shift masks by 4. + */ + lane_mask <<= 4; + polarity_mask <<= 4; + set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask); + set_field(&val, mipi_csi2->lane_polarities[lane + 1], + polarity_mask); + } + + reg_write(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), val); + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", + phy->instance, val); +} + static void cal_camerarx_enable(struct cal_camerarx *phy) { u32 num_lanes = phy->cal->data->camerarx[phy->instance].num_lanes; @@ -500,88 +548,53 @@ static void cal_camerarx_disable(struct cal_camerarx *phy) } /* - * Errata i913: CSI2 LDO Needs to be disabled when module is powered on - * - * Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2 - * LDOs on the device are disabled if CSI-2 module is powered on - * (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304 - * | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high - * current draw on the module supply in active mode. - * - * Errata does not apply when CSI-2 module is powered off - * (0x4845 B304 | 0x4845 B384 [28:27] = 0x0). - * - * SW Workaround: - * Set the following register bits to disable the LDO, - * which is essentially CSI2 REG10 bit 6: - * - * Core 0: 0x4845 B828 = 0x0000 0040 - * Core 1: 0x4845 B928 = 0x0000 0040 + * TCLK values are OK at their reset values */ -static void cal_camerarx_i913_errata(struct cal_camerarx *phy) -{ - u32 reg10 = reg_read(phy, CAL_CSI2_PHY_REG10); - - set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); - - phy_dbg(1, phy, "CSI2_%d_REG10 = 0x%08x\n", phy->instance, reg10); - reg_write(phy, CAL_CSI2_PHY_REG10, reg10); -} +#define TCLK_TERM 0 +#define TCLK_MISS 1 +#define TCLK_SETTLE 14 -/* - * Enable the expected IRQ sources - */ -static void cal_camerarx_enable_irqs(struct cal_camerarx *phy) +static void cal_camerarx_config(struct cal_camerarx *phy, + const struct cal_fmt *fmt) { - u32 val; + unsigned int reg0, reg1; + unsigned int ths_term, ths_settle; + unsigned int csi2_ddrclk_khz; + struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = + &phy->endpoint.bus.mipi_csi2; + u32 num_lanes = mipi_csi2->num_data_lanes; - const u32 cio_err_mask = - CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK | - CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK | - CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK | - CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; + /* DPHY timing configuration */ + /* CSI-2 is DDR and we only count used lanes. */ + csi2_ddrclk_khz = phy->external_rate / 1000 + / (2 * num_lanes) * fmt->bpp; + phy_dbg(1, phy, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); - /* Enable CIO error irqs */ - reg_write(phy->cal, CAL_HL_IRQENABLE_SET(0), - CAL_HL_IRQ_CIO_MASK(phy->instance)); - reg_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), - cio_err_mask); + /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ + ths_term = 20 * csi2_ddrclk_khz / 1000000; + phy_dbg(1, phy, "ths_term: %d (0x%02x)\n", ths_term, ths_term); - /* Always enable OCPO error */ - reg_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); + /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ + ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; + phy_dbg(1, phy, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); - /* Enable IRQ_WDMA_END 0/1 */ - val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - reg_write(phy->cal, CAL_HL_IRQENABLE_SET(1), val); - /* Enable IRQ_WDMA_START 0/1 */ - val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - reg_write(phy->cal, CAL_HL_IRQENABLE_SET(2), val); - /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); -} + reg0 = reg_read(phy, CAL_CSI2_PHY_REG0); + set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, + CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); + set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); + set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); -static void cal_camerarx_disable_irqs(struct cal_camerarx *phy) -{ - u32 val; + phy_dbg(1, phy, "CSI2_%d_REG0 = 0x%08x\n", phy->instance, reg0); + reg_write(phy, CAL_CSI2_PHY_REG0, reg0); - /* Disable CIO error irqs */ - reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), - CAL_HL_IRQ_CIO_MASK(phy->instance)); - reg_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), - 0); + reg1 = reg_read(phy, CAL_CSI2_PHY_REG1); + set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); + set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); + set_field(®1, TCLK_MISS, CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); + set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); - /* Disable IRQ_WDMA_END 0/1 */ - val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(1), val); - /* Disable IRQ_WDMA_START 0/1 */ - val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(2), val); - /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0); + phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x\n", phy->instance, reg1); + reg_write(phy, CAL_CSI2_PHY_REG1, reg1); } static void cal_camerarx_power(struct cal_camerarx *phy, bool enable) @@ -613,54 +626,63 @@ static void cal_camerarx_power(struct cal_camerarx *phy, bool enable) enable ? "up" : "down"); } -/* - * TCLK values are OK at their reset values - */ -#define TCLK_TERM 0 -#define TCLK_MISS 1 -#define TCLK_SETTLE 14 - -static void cal_camerarx_config(struct cal_camerarx *phy, - const struct cal_fmt *fmt) +static void cal_camerarx_wait_reset(struct cal_camerarx *phy) { - unsigned int reg0, reg1; - unsigned int ths_term, ths_settle; - unsigned int csi2_ddrclk_khz; - struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &phy->endpoint.bus.mipi_csi2; - u32 num_lanes = mipi_csi2->num_data_lanes; + unsigned long timeout; - /* DPHY timing configuration */ - /* CSI-2 is DDR and we only count used lanes. */ - csi2_ddrclk_khz = phy->external_rate / 1000 - / (2 * num_lanes) * fmt->bpp; - phy_dbg(1, phy, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); + timeout = jiffies + msecs_to_jiffies(750); + while (time_before(jiffies, timeout)) { + if (reg_read_field(phy->cal, + CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) + break; + usleep_range(500, 5000); + } - /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ - ths_term = 20 * csi2_ddrclk_khz / 1000000; - phy_dbg(1, phy, "ths_term: %d (0x%02x)\n", ths_term, ths_term); + if (reg_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) != + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) + phy_err(phy, "Timeout waiting for Complex IO reset done\n"); +} - /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ - ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; - phy_dbg(1, phy, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); +static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) +{ + unsigned long timeout; - reg0 = reg_read(phy, CAL_CSI2_PHY_REG0); - set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, - CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); - set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); - set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); + timeout = jiffies + msecs_to_jiffies(750); + while (time_before(jiffies, timeout)) { + if (reg_read_field(phy->cal, + CAL_CSI2_TIMING(phy->instance), + CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0) + break; + usleep_range(500, 5000); + } - phy_dbg(1, phy, "CSI2_%d_REG0 = 0x%08x\n", phy->instance, reg0); - reg_write(phy, CAL_CSI2_PHY_REG0, reg0); + if (reg_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance), + CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0) + phy_err(phy, "Timeout waiting for stop state\n"); +} - reg1 = reg_read(phy, CAL_CSI2_PHY_REG1); - set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); - set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); - set_field(®1, TCLK_MISS, CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); - set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); +static void cal_camerarx_wait_ready(struct cal_camerarx *phy) +{ + /* Steps + * 2. Wait for completion of reset + * Note if the external sensor is not sending byte clock, + * the reset will timeout + * 4.Force FORCERXMODE + * G. Wait for all enabled lane to reach stop state + * H. Disable pull down using pad control + */ - phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x\n", phy->instance, reg1); - reg_write(phy, CAL_CSI2_PHY_REG1, reg1); + /* 2. Wait for reset completion */ + cal_camerarx_wait_reset(phy); + + /* 4. G. Wait for all enabled lane to reach stop state */ + cal_camerarx_wait_stop_state(phy); + + phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x (Bit(31,28) should be set!)\n", + phy->instance, reg_read(phy, CAL_CSI2_PHY_REG1)); } static void cal_camerarx_init(struct cal_camerarx *phy, @@ -734,65 +756,6 @@ static void cal_camerarx_init(struct cal_camerarx *phy, cal_camerarx_power(phy, true); } -static void cal_camerarx_wait_reset(struct cal_camerarx *phy) -{ - unsigned long timeout; - - timeout = jiffies + msecs_to_jiffies(750); - while (time_before(jiffies, timeout)) { - if (reg_read_field(phy->cal, - CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) - break; - usleep_range(500, 5000); - } - - if (reg_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) != - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) - phy_err(phy, "Timeout waiting for Complex IO reset done\n"); -} - -static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) -{ - unsigned long timeout; - - timeout = jiffies + msecs_to_jiffies(750); - while (time_before(jiffies, timeout)) { - if (reg_read_field(phy->cal, - CAL_CSI2_TIMING(phy->instance), - CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0) - break; - usleep_range(500, 5000); - } - - if (reg_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance), - CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0) - phy_err(phy, "Timeout waiting for stop state\n"); -} - -static void cal_camerarx_wait_ready(struct cal_camerarx *phy) -{ - /* Steps - * 2. Wait for completion of reset - * Note if the external sensor is not sending byte clock, - * the reset will timeout - * 4.Force FORCERXMODE - * G. Wait for all enabled lane to reach stop state - * H. Disable pull down using pad control - */ - - /* 2. Wait for reset completion */ - cal_camerarx_wait_reset(phy); - - /* 4. G. Wait for all enabled lane to reach stop state */ - cal_camerarx_wait_stop_state(phy); - - phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x (Bit(31,28) should be set!)\n", - phy->instance, reg_read(phy, CAL_CSI2_PHY_REG1)); -} - static void cal_camerarx_deinit(struct cal_camerarx *phy) { unsigned int i; @@ -822,32 +785,89 @@ static void cal_camerarx_deinit(struct cal_camerarx *phy) cal_camerarx_disable(phy); } -static void cal_camerarx_lane_config(struct cal_camerarx *phy) +/* + * Errata i913: CSI2 LDO Needs to be disabled when module is powered on + * + * Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2 + * LDOs on the device are disabled if CSI-2 module is powered on + * (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304 + * | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high + * current draw on the module supply in active mode. + * + * Errata does not apply when CSI-2 module is powered off + * (0x4845 B304 | 0x4845 B384 [28:27] = 0x0). + * + * SW Workaround: + * Set the following register bits to disable the LDO, + * which is essentially CSI2 REG10 bit 6: + * + * Core 0: 0x4845 B828 = 0x0000 0040 + * Core 1: 0x4845 B928 = 0x0000 0040 + */ +static void cal_camerarx_i913_errata(struct cal_camerarx *phy) { - u32 val = reg_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)); - u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; - u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; - struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &phy->endpoint.bus.mipi_csi2; - int lane; + u32 reg10 = reg_read(phy, CAL_CSI2_PHY_REG10); - set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); - set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask); - for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) { - /* - * Every lane are one nibble apart starting with the - * clock followed by the data lanes so shift masks by 4. - */ - lane_mask <<= 4; - polarity_mask <<= 4; - set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask); - set_field(&val, mipi_csi2->lane_polarities[lane + 1], - polarity_mask); - } + set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); - reg_write(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), val); - phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", - phy->instance, val); + phy_dbg(1, phy, "CSI2_%d_REG10 = 0x%08x\n", phy->instance, reg10); + reg_write(phy, CAL_CSI2_PHY_REG10, reg10); +} + +/* + * Enable the expected IRQ sources + */ +static void cal_camerarx_enable_irqs(struct cal_camerarx *phy) +{ + u32 val; + + const u32 cio_err_mask = + CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK | + CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK | + CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK | + CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; + + /* Enable CIO error irqs */ + reg_write(phy->cal, CAL_HL_IRQENABLE_SET(0), + CAL_HL_IRQ_CIO_MASK(phy->instance)); + reg_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), + cio_err_mask); + + /* Always enable OCPO error */ + reg_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); + + /* Enable IRQ_WDMA_END 0/1 */ + val = 0; + set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + reg_write(phy->cal, CAL_HL_IRQENABLE_SET(1), val); + /* Enable IRQ_WDMA_START 0/1 */ + val = 0; + set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + reg_write(phy->cal, CAL_HL_IRQENABLE_SET(2), val); + /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ + reg_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); +} + +static void cal_camerarx_disable_irqs(struct cal_camerarx *phy) +{ + u32 val; + + /* Disable CIO error irqs */ + reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), + CAL_HL_IRQ_CIO_MASK(phy->instance)); + reg_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), + 0); + + /* Disable IRQ_WDMA_END 0/1 */ + val = 0; + set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(1), val); + /* Disable IRQ_WDMA_START 0/1 */ + val = 0; + set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(2), val); + /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ + reg_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0); } static void cal_camerarx_ppi_enable(struct cal_camerarx *phy) @@ -863,26 +883,6 @@ static void cal_camerarx_ppi_disable(struct cal_camerarx *phy) 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } -static int cal_camerarx_get_external_info(struct cal_camerarx *phy) -{ - struct v4l2_ctrl *ctrl; - - if (!phy->sensor) - return -ENODEV; - - ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); - if (!ctrl) { - phy_err(phy, "no pixel rate control in subdev: %s\n", - phy->sensor->name); - return -EPIPE; - } - - phy->external_rate = v4l2_ctrl_g_ctrl_int64(ctrl); - phy_dbg(3, phy, "sensor Pixel Rate: %u\n", phy->external_rate); - - return 0; -} - static int cal_camerarx_regmap_init(struct cal_dev *cal, struct cal_camerarx *phy) { -- cgit v1.2.3 From 49b184a0dae93b7eaa3dbd09c0287db9a471c9da Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:39 +0200 Subject: media: ti-vpe: cal: Refactor camerarx start and stop Refactor the camerarx start and stop procedure to group all the corresponding operations in two functions, cal_camerarx_start() and cal_camerarx_stop() instead of splitting them in multiple steps called from the vb2 stream start and stop functions. This reduces the coupling between the camerarx and the contexts, and better models the camerarx hardware. The cal_camerarx_start() is a reworked version of cal_camerarx_init() with the description of the start procedure updated to match the AM65x, DRA7[124567]x and DRA80xM TRMs. The cal_camerarx_wait_ready() function is inlined in cal_camerarx_start() to better describe the start procedure. No functional change is included in the camerarx start and stop procedures themselves, but the interleaving of the start steps with the context configuration has been changed in cal_start_streaming(). Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 170 ++++++++++++++++++++---------------- 1 file changed, 94 insertions(+), 76 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 6b0ed2946eba..50fe6c19c417 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -664,54 +664,50 @@ static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) phy_err(phy, "Timeout waiting for stop state\n"); } -static void cal_camerarx_wait_ready(struct cal_camerarx *phy) -{ - /* Steps - * 2. Wait for completion of reset - * Note if the external sensor is not sending byte clock, - * the reset will timeout - * 4.Force FORCERXMODE - * G. Wait for all enabled lane to reach stop state - * H. Disable pull down using pad control - */ - - /* 2. Wait for reset completion */ - cal_camerarx_wait_reset(phy); - - /* 4. G. Wait for all enabled lane to reach stop state */ - cal_camerarx_wait_stop_state(phy); - - phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x (Bit(31,28) should be set!)\n", - phy->instance, reg_read(phy, CAL_CSI2_PHY_REG1)); -} - -static void cal_camerarx_init(struct cal_camerarx *phy, +static int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) { - u32 val; u32 sscounter; + u32 val; + int ret; + + ret = cal_camerarx_get_external_info(phy); + if (ret < 0) + return ret; + + ret = v4l2_subdev_call(phy->sensor, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { + phy_err(phy, "power on failed in subdev\n"); + return ret; + } - /* Steps - * 1. Configure D-PHY mode and enable required lanes - * 2. Reset complex IO - Wait for completion of reset - * Note if the external sensor is not sending byte clock, - * the reset will timeout - * 3 Program Stop States - * A. Program THS_TERM, THS_SETTLE, etc... Timings parameters - * in terms of DDR clock periods - * B. Enable stop state transition timeouts - * 4.Force FORCERXMODE - * D. Enable pull down using pad control - * E. Power up PHY - * F. Wait for power up completion - * G. Wait for all enabled lane to reach stop state - * H. Disable pull down using pad control + /* + * CSI-2 PHY Link Initialization Sequence, according to the DRA74xP / + * DRA75xP / DRA76xP / DRA77xP TRM. The DRA71x / DRA72x and the AM65x / + * DRA80xM TRMs have a a slightly simplified sequence. */ - /* 1. Configure D-PHY mode and enable required lanes */ + /* + * 1. Configure all CSI-2 low level protocol registers to be ready to + * receive signals/data from the CSI-2 PHY. + * + * i.-v. Configure the lanes position and polarity. + */ + cal_camerarx_lane_config(phy); + + /* + * vi.-vii. Configure D-PHY mode, enable the required lanes and + * enable the CAMERARX clock. + */ cal_camerarx_enable(phy); - /* 2. Reset complex IO - Do not wait for reset completion */ + /* + * 2. CSI PHY and link initialization sequence. + * + * a. Deassert the CSI-2 PHY reset. Do not wait for reset completion + * at this point, as it requires the external sensor to send the + * CSI-2 HS clock. + */ reg_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); @@ -719,19 +715,20 @@ static void cal_camerarx_init(struct cal_camerarx *phy, phy->instance, reg_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance))); - /* Dummy read to allow SCP reset to complete */ + /* Dummy read to allow SCP reset to complete. */ reg_read(phy, CAL_CSI2_PHY_REG0); - /* 3.A. Program Phy Timing Parameters */ + /* Program the PHY timing parameters. */ cal_camerarx_config(phy, fmt); - /* 3.B. Program Stop States */ /* + * b. Assert the FORCERXMODE signal. + * * The stop-state-counter is based on fclk cycles, and we always use * the x16 and x4 settings, so stop-state-timeout = * fclk-cycle * 16 * 4 * counter. * - * Stop-state-timeout must be more than 100us as per CSI2 spec, so we + * Stop-state-timeout must be more than 100us as per CSI-2 spec, so we * calculate a timeout that's 100us (rounding up). */ sscounter = DIV_ROUND_UP(clk_get_rate(phy->cal->fclk), 10000 * 16 * 4); @@ -745,24 +742,63 @@ static void cal_camerarx_init(struct cal_camerarx *phy, phy->instance, reg_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); - /* 4. Force FORCERXMODE */ + /* Assert the FORCERXMODE signal. */ reg_write_field(phy->cal, CAL_CSI2_TIMING(phy->instance), 1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK); phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n", phy->instance, reg_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); - /* E. Power up the PHY using the complex IO */ + /* + * c. Connect pull-down on CSI-2 PHY link (using pad control). + * + * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not + * implemented. + */ + + /* + * d. Power up the CSI-2 PHY. + * e. Check whether the state status reaches the ON state. + */ cal_camerarx_power(phy, true); + + /* + * Start the sensor to enable the CSI-2 HS clock. We can now wait for + * CSI-2 PHY reset to complete. + */ + ret = v4l2_subdev_call(phy->sensor, video, s_stream, 1); + if (ret) { + v4l2_subdev_call(phy->sensor, core, s_power, 0); + phy_err(phy, "stream on failed in subdev\n"); + return ret; + } + + cal_camerarx_wait_reset(phy); + + /* f. Wait for STOPSTATE=1 for all enabled lane modules. */ + cal_camerarx_wait_stop_state(phy); + + phy_dbg(1, phy, "CSI2_%u_REG1 = 0x%08x (bits 31-28 should be set)\n", + phy->instance, reg_read(phy, CAL_CSI2_PHY_REG1)); + + /* + * g. Disable pull-down on CSI-2 PHY link (using pad control). + * + * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not + * implemented. + */ + + return 0; } -static void cal_camerarx_deinit(struct cal_camerarx *phy) +static void cal_camerarx_stop(struct cal_camerarx *phy) { unsigned int i; + int ret; cal_camerarx_power(phy, false); - /* Assert Comple IO Reset */ + /* Assert Complex IO Reset */ reg_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); @@ -783,6 +819,13 @@ static void cal_camerarx_deinit(struct cal_camerarx *phy) /* Disable the phy */ cal_camerarx_disable(phy); + + if (v4l2_subdev_call(phy->sensor, video, s_stream, 0)) + phy_err(phy, "stream off failed in subdev\n"); + + ret = v4l2_subdev_call(phy->sensor, core, s_power, 0); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + phy_err(phy, "power off failed in subdev\n"); } /* @@ -1809,36 +1852,19 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0); ctx->sequence = 0; - ret = cal_camerarx_get_external_info(ctx->phy); - if (ret < 0) - goto err; - - ret = v4l2_subdev_call(ctx->phy->sensor, core, s_power, 1); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { - ctx_err(ctx, "power on failed in subdev\n"); - goto err; - } - pm_runtime_get_sync(ctx->cal->dev); cal_ctx_csi2_config(ctx); cal_ctx_pix_proc_config(ctx); cal_ctx_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline, ctx->v_fmt.fmt.pix.height); - cal_camerarx_lane_config(ctx->phy); cal_camerarx_enable_irqs(ctx->phy); - cal_camerarx_init(ctx->phy, ctx->fmt); - ret = v4l2_subdev_call(ctx->phy->sensor, video, s_stream, 1); - if (ret) { - v4l2_subdev_call(ctx->phy->sensor, core, s_power, 0); - ctx_err(ctx, "stream on failed in subdev\n"); - pm_runtime_put_sync(ctx->cal->dev); + ret = cal_camerarx_start(ctx->phy, ctx->fmt); + if (ret) goto err; - } - cal_camerarx_wait_ready(ctx->phy); cal_ctx_wr_dma_addr(ctx, addr); cal_camerarx_ppi_enable(ctx->phy); @@ -1867,7 +1893,6 @@ static void cal_stop_streaming(struct vb2_queue *vq) struct cal_buffer *buf, *tmp; unsigned long timeout; unsigned long flags; - int ret; bool dma_act; cal_camerarx_ppi_disable(ctx->phy); @@ -1887,14 +1912,7 @@ static void cal_stop_streaming(struct vb2_queue *vq) ctx_err(ctx, "failed to disable dma cleanly\n"); cal_camerarx_disable_irqs(ctx->phy); - cal_camerarx_deinit(ctx->phy); - - if (v4l2_subdev_call(ctx->phy->sensor, video, s_stream, 0)) - ctx_err(ctx, "stream off failed in subdev\n"); - - ret = v4l2_subdev_call(ctx->phy->sensor, core, s_power, 0); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) - ctx_err(ctx, "power off failed in subdev\n"); + cal_camerarx_stop(ctx->phy); /* Release all active buffers */ spin_lock_irqsave(&ctx->slock, flags); -- cgit v1.2.3 From 399b0a3d54b695def41a77a05ad8221c0e9ffc29 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:40 +0200 Subject: media: ti-vpe: cal: Don't store external rate in cal_camerarx The external pixel rate is retrieved when starting the camerarx and only used then. There's no need to store it in the cal_camerarx structure, it can be returned by cal_camerarx_get_external_info() and explicitly passed to cal_camerarx_config(). While at it, rename cal_camerarx_get_external_info() to cal_camerarx_get_external_rate() to better reflect the function's purpose. Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 50fe6c19c417..8b1815bbf1a7 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -272,7 +272,6 @@ struct cal_camerarx { struct v4l2_fwnode_endpoint endpoint; struct device_node *sensor_node; struct v4l2_subdev *sensor; - unsigned int external_rate; }; struct cal_dev { @@ -481,9 +480,10 @@ static void cal_quickdump_regs(struct cal_dev *cal) * ------------------------------------------------------------------ */ -static int cal_camerarx_get_external_info(struct cal_camerarx *phy) +static s64 cal_camerarx_get_external_rate(struct cal_camerarx *phy) { struct v4l2_ctrl *ctrl; + s64 rate; if (!phy->sensor) return -ENODEV; @@ -495,10 +495,10 @@ static int cal_camerarx_get_external_info(struct cal_camerarx *phy) return -EPIPE; } - phy->external_rate = v4l2_ctrl_g_ctrl_int64(ctrl); - phy_dbg(3, phy, "sensor Pixel Rate: %u\n", phy->external_rate); + rate = v4l2_ctrl_g_ctrl_int64(ctrl); + phy_dbg(3, phy, "sensor Pixel Rate: %llu\n", rate); - return 0; + return rate; } static void cal_camerarx_lane_config(struct cal_camerarx *phy) @@ -554,7 +554,7 @@ static void cal_camerarx_disable(struct cal_camerarx *phy) #define TCLK_MISS 1 #define TCLK_SETTLE 14 -static void cal_camerarx_config(struct cal_camerarx *phy, +static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate, const struct cal_fmt *fmt) { unsigned int reg0, reg1; @@ -565,9 +565,16 @@ static void cal_camerarx_config(struct cal_camerarx *phy, u32 num_lanes = mipi_csi2->num_data_lanes; /* DPHY timing configuration */ - /* CSI-2 is DDR and we only count used lanes. */ - csi2_ddrclk_khz = phy->external_rate / 1000 - / (2 * num_lanes) * fmt->bpp; + + /* + * CSI-2 is DDR and we only count used lanes. + * + * csi2_ddrclk_khz = external_rate / 1000 + * / (2 * num_lanes) * fmt->bpp; + */ + csi2_ddrclk_khz = div_s64(external_rate * fmt->bpp, + 2 * num_lanes * 1000); + phy_dbg(1, phy, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ @@ -667,13 +674,14 @@ static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) static int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) { + s64 external_rate; u32 sscounter; u32 val; int ret; - ret = cal_camerarx_get_external_info(phy); - if (ret < 0) - return ret; + external_rate = cal_camerarx_get_external_rate(phy); + if (external_rate < 0) + return external_rate; ret = v4l2_subdev_call(phy->sensor, core, s_power, 1); if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { @@ -719,7 +727,7 @@ static int cal_camerarx_start(struct cal_camerarx *phy, reg_read(phy, CAL_CSI2_PHY_REG0); /* Program the PHY timing parameters. */ - cal_camerarx_config(phy, fmt); + cal_camerarx_config(phy, external_rate, fmt); /* * b. Assert the FORCERXMODE signal. @@ -1039,7 +1047,6 @@ static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, phy->cal = cal; phy->instance = instance; - phy->external_rate = 192000000; phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, (instance == 0) ? -- cgit v1.2.3 From 56d1d67468ae195613c374e1e42fac85d22f24e5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:41 +0200 Subject: media: ti-vpe: cal: Remove unneeded phy->sensor NULL check The phy->sensor NULL check in cal_camerarx_get_external_rate() is not needed, as the V4L2 video devices are only registered when the sensor is bound. Remove it. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 8b1815bbf1a7..ade76739de47 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -485,9 +485,6 @@ static s64 cal_camerarx_get_external_rate(struct cal_camerarx *phy) struct v4l2_ctrl *ctrl; s64 rate; - if (!phy->sensor) - return -ENODEV; - ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); if (!ctrl) { phy_err(phy, "no pixel rate control in subdev: %s\n", -- cgit v1.2.3 From f4d9837d872523e8ef8bd1e0156eb90e2fed8dc3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:42 +0200 Subject: media: ti-vpe: cal: Use 'unsigned int' type instead of 'unsigned' Specifying 'int' explicitly is generally preferred in the kernel for unsigned int types. Fix the only wrong occurrence. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index ade76739de47..fdb40c85de9b 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1776,7 +1776,7 @@ static int cal_queue_setup(struct vb2_queue *vq, unsigned int sizes[], struct device *alloc_devs[]) { struct cal_ctx *ctx = vb2_get_drv_priv(vq); - unsigned size = ctx->v_fmt.fmt.pix.sizeimage; + unsigned int size = ctx->v_fmt.fmt.pix.sizeimage; if (vq->num_buffers + *nbuffers < 3) *nbuffers = 3 - vq->num_buffers; -- cgit v1.2.3 From 9d551891473e35eae4ea3b21cac30136f455d405 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:43 +0200 Subject: media: ti-vpe: cal: Split video node handling to cal-video.c To prepare for implementation of media controller centric device configuration, move all the video node handling support to a separate file. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/Makefile | 2 +- drivers/media/platform/ti-vpe/cal-video.c | 887 +++++++++++++++++++++++ drivers/media/platform/ti-vpe/cal.c | 1085 +---------------------------- drivers/media/platform/ti-vpe/cal.h | 226 ++++++ 4 files changed, 1137 insertions(+), 1063 deletions(-) create mode 100644 drivers/media/platform/ti-vpe/cal-video.c create mode 100644 drivers/media/platform/ti-vpe/cal.h (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/Makefile b/drivers/media/platform/ti-vpe/Makefile index 886ac5ec073f..a7bf8f2a1386 100644 --- a/drivers/media/platform/ti-vpe/Makefile +++ b/drivers/media/platform/ti-vpe/Makefile @@ -13,4 +13,4 @@ ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG obj-$(CONFIG_VIDEO_TI_CAL) += ti-cal.o -ti-cal-y := cal.o +ti-cal-y := cal.o cal-video.o diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c new file mode 100644 index 000000000000..82b32a2f9db5 --- /dev/null +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -0,0 +1,887 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI Camera Access Layer (CAL) - Video Device + * + * Copyright (c) 2015-2020 Texas Instruments Inc. + * + * Authors: + * Benoit Parrot + * Laurent Pinchart + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cal.h" + +/* ------------------------------------------------------------------ + * Format Handling + * ------------------------------------------------------------------ + */ + +static const struct cal_fmt cal_formats[] = { + { + .fourcc = V4L2_PIX_FMT_YUYV, + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_YVYU, + .code = MEDIA_BUS_FMT_YVYU8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_VYUY, + .code = MEDIA_BUS_FMT_VYUY8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ + .code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ + .code = MEDIA_BUS_FMT_RGB888_2X12_LE, + .bpp = 24, + }, { + .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ + .code = MEDIA_BUS_FMT_RGB888_2X12_BE, + .bpp = 24, + }, { + .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ + .code = MEDIA_BUS_FMT_ARGB8888_1X32, + .bpp = 32, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .bpp = 12, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .bpp = 12, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .bpp = 12, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB12, + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .bpp = 12, + }, +}; + +/* Print Four-character-code (FOURCC) */ +static char *fourcc_to_str(u32 fmt) +{ + static char code[5]; + + code[0] = (unsigned char)(fmt & 0xff); + code[1] = (unsigned char)((fmt >> 8) & 0xff); + code[2] = (unsigned char)((fmt >> 16) & 0xff); + code[3] = (unsigned char)((fmt >> 24) & 0xff); + code[4] = '\0'; + + return code; +} + +/* ------------------------------------------------------------------ + * V4L2 Video IOCTLs + * ------------------------------------------------------------------ + */ + +static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx, + u32 pixelformat) +{ + const struct cal_fmt *fmt; + unsigned int k; + + for (k = 0; k < ctx->num_active_fmt; k++) { + fmt = ctx->active_fmt[k]; + if (fmt->fourcc == pixelformat) + return fmt; + } + + return NULL; +} + +static const struct cal_fmt *find_format_by_code(struct cal_ctx *ctx, + u32 code) +{ + const struct cal_fmt *fmt; + unsigned int k; + + for (k = 0; k < ctx->num_active_fmt; k++) { + fmt = ctx->active_fmt[k]; + if (fmt->code == code) + return fmt; + } + + return NULL; +} + +static int cal_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct cal_ctx *ctx = video_drvdata(file); + + strscpy(cap->driver, CAL_MODULE_NAME, sizeof(cap->driver)); + strscpy(cap->card, CAL_MODULE_NAME, sizeof(cap->card)); + + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", dev_name(ctx->cal->dev)); + return 0; +} + +static int cal_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct cal_ctx *ctx = video_drvdata(file); + const struct cal_fmt *fmt; + + if (f->index >= ctx->num_active_fmt) + return -EINVAL; + + fmt = ctx->active_fmt[f->index]; + + f->pixelformat = fmt->fourcc; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + return 0; +} + +static int __subdev_get_format(struct cal_ctx *ctx, + struct v4l2_mbus_framefmt *fmt) +{ + struct v4l2_subdev_format sd_fmt; + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; + int ret; + + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sd_fmt.pad = 0; + + ret = v4l2_subdev_call(ctx->phy->sensor, pad, get_fmt, NULL, &sd_fmt); + if (ret) + return ret; + + *fmt = *mbus_fmt; + + ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, + fmt->width, fmt->height, fmt->code); + + return 0; +} + +static int __subdev_set_format(struct cal_ctx *ctx, + struct v4l2_mbus_framefmt *fmt) +{ + struct v4l2_subdev_format sd_fmt; + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; + int ret; + + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sd_fmt.pad = 0; + *mbus_fmt = *fmt; + + ret = v4l2_subdev_call(ctx->phy->sensor, pad, set_fmt, NULL, &sd_fmt); + if (ret) + return ret; + + ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, + fmt->width, fmt->height, fmt->code); + + return 0; +} + +static int cal_calc_format_size(struct cal_ctx *ctx, + const struct cal_fmt *fmt, + struct v4l2_format *f) +{ + u32 bpl, max_width; + + if (!fmt) { + ctx_dbg(3, ctx, "No cal_fmt provided!\n"); + return -EINVAL; + } + + /* + * Maximum width is bound by the DMA max width in bytes. + * We need to recalculate the actual maxi width depending on the + * number of bytes per pixels required. + */ + max_width = MAX_WIDTH_BYTES / (ALIGN(fmt->bpp, 8) >> 3); + v4l_bound_align_image(&f->fmt.pix.width, 48, max_width, 2, + &f->fmt.pix.height, 32, MAX_HEIGHT_LINES, 0, 0); + + bpl = (f->fmt.pix.width * ALIGN(fmt->bpp, 8)) >> 3; + f->fmt.pix.bytesperline = ALIGN(bpl, 16); + + f->fmt.pix.sizeimage = f->fmt.pix.height * + f->fmt.pix.bytesperline; + + ctx_dbg(3, ctx, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n", + __func__, fourcc_to_str(f->fmt.pix.pixelformat), + f->fmt.pix.width, f->fmt.pix.height, + f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); + + return 0; +} + +static int cal_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cal_ctx *ctx = video_drvdata(file); + + *f = ctx->v_fmt; + + return 0; +} + +static int cal_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cal_ctx *ctx = video_drvdata(file); + const struct cal_fmt *fmt; + struct v4l2_subdev_frame_size_enum fse; + int ret, found; + + fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); + if (!fmt) { + ctx_dbg(3, ctx, "Fourcc format (0x%08x) not found.\n", + f->fmt.pix.pixelformat); + + /* Just get the first one enumerated */ + fmt = ctx->active_fmt[0]; + f->fmt.pix.pixelformat = fmt->fourcc; + } + + f->fmt.pix.field = ctx->v_fmt.fmt.pix.field; + + /* check for/find a valid width/height */ + ret = 0; + found = false; + fse.pad = 0; + fse.code = fmt->code; + fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; + for (fse.index = 0; ; fse.index++) { + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, + NULL, &fse); + if (ret) + break; + + if ((f->fmt.pix.width == fse.max_width) && + (f->fmt.pix.height == fse.max_height)) { + found = true; + break; + } else if ((f->fmt.pix.width >= fse.min_width) && + (f->fmt.pix.width <= fse.max_width) && + (f->fmt.pix.height >= fse.min_height) && + (f->fmt.pix.height <= fse.max_height)) { + found = true; + break; + } + } + + if (!found) { + /* use existing values as default */ + f->fmt.pix.width = ctx->v_fmt.fmt.pix.width; + f->fmt.pix.height = ctx->v_fmt.fmt.pix.height; + } + + /* + * Use current colorspace for now, it will get + * updated properly during s_fmt + */ + f->fmt.pix.colorspace = ctx->v_fmt.fmt.pix.colorspace; + return cal_calc_format_size(ctx, fmt, f); +} + +static int cal_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cal_ctx *ctx = video_drvdata(file); + struct vb2_queue *q = &ctx->vb_vidq; + const struct cal_fmt *fmt; + struct v4l2_mbus_framefmt mbus_fmt; + int ret; + + if (vb2_is_busy(q)) { + ctx_dbg(3, ctx, "%s device busy\n", __func__); + return -EBUSY; + } + + ret = cal_try_fmt_vid_cap(file, priv, f); + if (ret < 0) + return ret; + + fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); + + v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code); + + ret = __subdev_set_format(ctx, &mbus_fmt); + if (ret) + return ret; + + /* Just double check nothing has gone wrong */ + if (mbus_fmt.code != fmt->code) { + ctx_dbg(3, ctx, + "%s subdev changed format on us, this should not happen\n", + __func__); + return -EINVAL; + } + + v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); + ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; + cal_calc_format_size(ctx, fmt, &ctx->v_fmt); + ctx->fmt = fmt; + ctx->m_fmt = mbus_fmt; + *f = ctx->v_fmt; + + return 0; +} + +static int cal_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct cal_ctx *ctx = video_drvdata(file); + const struct cal_fmt *fmt; + struct v4l2_subdev_frame_size_enum fse; + int ret; + + /* check for valid format */ + fmt = find_format_by_pix(ctx, fsize->pixel_format); + if (!fmt) { + ctx_dbg(3, ctx, "Invalid pixel code: %x\n", + fsize->pixel_format); + return -EINVAL; + } + + fse.index = fsize->index; + fse.pad = 0; + fse.code = fmt->code; + fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; + + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, NULL, + &fse); + if (ret) + return ret; + + ctx_dbg(1, ctx, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", + __func__, fse.index, fse.code, fse.min_width, fse.max_width, + fse.min_height, fse.max_height); + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = fse.max_width; + fsize->discrete.height = fse.max_height; + + return 0; +} + +static int cal_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + if (inp->index > 0) + return -EINVAL; + + inp->type = V4L2_INPUT_TYPE_CAMERA; + sprintf(inp->name, "Camera %u", inp->index); + return 0; +} + +static int cal_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int cal_s_input(struct file *file, void *priv, unsigned int i) +{ + return i > 0 ? -EINVAL : 0; +} + +/* timeperframe is arbitrary and continuous */ +static int cal_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fival) +{ + struct cal_ctx *ctx = video_drvdata(file); + const struct cal_fmt *fmt; + struct v4l2_subdev_frame_interval_enum fie = { + .index = fival->index, + .width = fival->width, + .height = fival->height, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + int ret; + + fmt = find_format_by_pix(ctx, fival->pixel_format); + if (!fmt) + return -EINVAL; + + fie.code = fmt->code; + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_interval, + NULL, &fie); + if (ret) + return ret; + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = fie.interval; + + return 0; +} + +static const struct v4l2_file_operations cal_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .mmap = vb2_fop_mmap, +}; + +static const struct v4l2_ioctl_ops cal_ioctl_ops = { + .vidioc_querycap = cal_querycap, + .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap, + .vidioc_enum_framesizes = cal_enum_framesizes, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_enum_input = cal_enum_input, + .vidioc_g_input = cal_g_input, + .vidioc_s_input = cal_s_input, + .vidioc_enum_frameintervals = cal_enum_frameintervals, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* ------------------------------------------------------------------ + * videobuf2 Operations + * ------------------------------------------------------------------ + */ + +static int cal_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vq); + unsigned int size = ctx->v_fmt.fmt.pix.sizeimage; + + if (vq->num_buffers + *nbuffers < 3) + *nbuffers = 3 - vq->num_buffers; + + if (*nplanes) { + if (sizes[0] < size) + return -EINVAL; + size = sizes[0]; + } + + *nplanes = 1; + sizes[0] = size; + + ctx_dbg(3, ctx, "nbuffers=%d, size=%d\n", *nbuffers, sizes[0]); + + return 0; +} + +static int cal_buffer_prepare(struct vb2_buffer *vb) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct cal_buffer *buf = container_of(vb, struct cal_buffer, + vb.vb2_buf); + unsigned long size; + + if (WARN_ON(!ctx->fmt)) + return -EINVAL; + + size = ctx->v_fmt.fmt.pix.sizeimage; + if (vb2_plane_size(vb, 0) < size) { + ctx_err(ctx, + "data will not fit into plane (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); + return 0; +} + +static void cal_buffer_queue(struct vb2_buffer *vb) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct cal_buffer *buf = container_of(vb, struct cal_buffer, + vb.vb2_buf); + struct cal_dmaqueue *vidq = &ctx->vidq; + unsigned long flags; + + /* recheck locking */ + spin_lock_irqsave(&ctx->slock, flags); + list_add_tail(&buf->list, &vidq->active); + spin_unlock_irqrestore(&ctx->slock, flags); +} + +static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vq); + struct cal_dmaqueue *dma_q = &ctx->vidq; + struct cal_buffer *buf, *tmp; + unsigned long addr; + unsigned long flags; + int ret; + + spin_lock_irqsave(&ctx->slock, flags); + if (list_empty(&dma_q->active)) { + spin_unlock_irqrestore(&ctx->slock, flags); + ctx_dbg(3, ctx, "buffer queue is empty\n"); + return -EIO; + } + + buf = list_entry(dma_q->active.next, struct cal_buffer, list); + ctx->cur_frm = buf; + ctx->next_frm = buf; + list_del(&buf->list); + spin_unlock_irqrestore(&ctx->slock, flags); + + addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0); + ctx->sequence = 0; + + pm_runtime_get_sync(ctx->cal->dev); + + cal_ctx_csi2_config(ctx); + cal_ctx_pix_proc_config(ctx); + cal_ctx_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline, + ctx->v_fmt.fmt.pix.height); + + cal_camerarx_enable_irqs(ctx->phy); + + ret = cal_camerarx_start(ctx->phy, ctx->fmt); + if (ret) + goto err; + + cal_ctx_wr_dma_addr(ctx, addr); + cal_camerarx_ppi_enable(ctx->phy); + + if (cal_debug >= 4) + cal_quickdump_regs(ctx->cal); + + return 0; + +err: + spin_lock_irqsave(&ctx->slock, flags); + vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + ctx->cur_frm = NULL; + ctx->next_frm = NULL; + list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + } + spin_unlock_irqrestore(&ctx->slock, flags); + return ret; +} + +static void cal_stop_streaming(struct vb2_queue *vq) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vq); + struct cal_dmaqueue *dma_q = &ctx->vidq; + struct cal_buffer *buf, *tmp; + unsigned long timeout; + unsigned long flags; + bool dma_act; + + cal_camerarx_ppi_disable(ctx->phy); + + /* wait for stream and dma to finish */ + dma_act = true; + timeout = jiffies + msecs_to_jiffies(500); + while (dma_act && time_before(jiffies, timeout)) { + msleep(50); + + spin_lock_irqsave(&ctx->slock, flags); + dma_act = ctx->dma_act; + spin_unlock_irqrestore(&ctx->slock, flags); + } + + if (dma_act) + ctx_err(ctx, "failed to disable dma cleanly\n"); + + cal_camerarx_disable_irqs(ctx->phy); + cal_camerarx_stop(ctx->phy); + + /* Release all active buffers */ + spin_lock_irqsave(&ctx->slock, flags); + list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + + if (ctx->cur_frm == ctx->next_frm) { + vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } else { + vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&ctx->next_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); + } + ctx->cur_frm = NULL; + ctx->next_frm = NULL; + spin_unlock_irqrestore(&ctx->slock, flags); + + pm_runtime_put_sync(ctx->cal->dev); +} + +static const struct vb2_ops cal_video_qops = { + .queue_setup = cal_queue_setup, + .buf_prepare = cal_buffer_prepare, + .buf_queue = cal_buffer_queue, + .start_streaming = cal_start_streaming, + .stop_streaming = cal_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +/* ------------------------------------------------------------------ + * V4L2 Initialization and Registration + * ------------------------------------------------------------------ + */ + +static const struct video_device cal_videodev = { + .name = CAL_MODULE_NAME, + .fops = &cal_fops, + .ioctl_ops = &cal_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE, +}; + +static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) +{ + struct v4l2_subdev_mbus_code_enum mbus_code; + struct v4l2_mbus_framefmt mbus_fmt; + const struct cal_fmt *fmt; + unsigned int i, j, k; + int ret = 0; + + /* Enumerate sub device formats and enable all matching local formats */ + ctx->active_fmt = devm_kcalloc(ctx->cal->dev, ARRAY_SIZE(cal_formats), + sizeof(*ctx->active_fmt), GFP_KERNEL); + ctx->num_active_fmt = 0; + + for (j = 0, i = 0; ret != -EINVAL; ++j) { + + memset(&mbus_code, 0, sizeof(mbus_code)); + mbus_code.index = j; + mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_mbus_code, + NULL, &mbus_code); + if (ret) + continue; + + ctx_dbg(2, ctx, + "subdev %s: code: %04x idx: %u\n", + ctx->phy->sensor->name, mbus_code.code, j); + + for (k = 0; k < ARRAY_SIZE(cal_formats); k++) { + const struct cal_fmt *fmt = &cal_formats[k]; + + if (mbus_code.code == fmt->code) { + ctx->active_fmt[i] = fmt; + ctx_dbg(2, ctx, + "matched fourcc: %s: code: %04x idx: %u\n", + fourcc_to_str(fmt->fourcc), + fmt->code, i); + ctx->num_active_fmt = ++i; + } + } + } + + if (i == 0) { + ctx_err(ctx, "No suitable format reported by subdev %s\n", + ctx->phy->sensor->name); + return -EINVAL; + } + + ret = __subdev_get_format(ctx, &mbus_fmt); + if (ret) + return ret; + + fmt = find_format_by_code(ctx, mbus_fmt.code); + if (!fmt) { + ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n", + mbus_fmt.code); + return -EINVAL; + } + + /* Save current subdev format */ + v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); + ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; + cal_calc_format_size(ctx, fmt, &ctx->v_fmt); + ctx->fmt = fmt; + ctx->m_fmt = mbus_fmt; + + return 0; +} + +int cal_ctx_v4l2_register(struct cal_ctx *ctx) +{ + struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; + struct video_device *vfd = &ctx->vdev; + int ret; + + ret = cal_ctx_v4l2_init_formats(ctx); + if (ret) + return ret; + + ret = v4l2_ctrl_add_handler(hdl, ctx->phy->sensor->ctrl_handler, NULL, + true); + if (ret < 0) { + ctx_err(ctx, "Failed to add sensor ctrl handler\n"); + return ret; + } + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, cal_video_nr); + if (ret < 0) { + ctx_err(ctx, "Failed to register video device\n"); + return ret; + } + + ctx_info(ctx, "V4L2 device registered as %s\n", + video_device_node_name(vfd)); + + return 0; +} + +void cal_ctx_v4l2_unregister(struct cal_ctx *ctx) +{ + ctx_dbg(1, ctx, "unregistering %s\n", + video_device_node_name(&ctx->vdev)); + + video_unregister_device(&ctx->vdev); +} + +int cal_ctx_v4l2_init(struct cal_ctx *ctx) +{ + struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; + struct video_device *vfd = &ctx->vdev; + struct vb2_queue *q = &ctx->vb_vidq; + int ret; + + INIT_LIST_HEAD(&ctx->vidq.active); + spin_lock_init(&ctx->slock); + mutex_init(&ctx->mutex); + + /* Initialize the vb2 queue. */ + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + q->drv_priv = ctx; + q->buf_struct_size = sizeof(struct cal_buffer); + q->ops = &cal_video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &ctx->mutex; + q->min_buffers_needed = 3; + q->dev = ctx->cal->dev; + + ret = vb2_queue_init(q); + if (ret) + return ret; + + /* Initialize the video device and media entity. */ + *vfd = cal_videodev; + vfd->v4l2_dev = &ctx->cal->v4l2_dev; + vfd->queue = q; + snprintf(vfd->name, sizeof(vfd->name), "CAL output %u", ctx->index); + vfd->lock = &ctx->mutex; + video_set_drvdata(vfd, ctx); + + ctx->pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&vfd->entity, 1, &ctx->pad); + if (ret < 0) + return ret; + + /* Initialize the control handler. */ + ret = v4l2_ctrl_handler_init(hdl, 11); + if (ret < 0) { + ctx_err(ctx, "Failed to init ctrl handler\n"); + goto error; + } + + vfd->ctrl_handler = hdl; + + return 0; + +error: + media_entity_cleanup(&vfd->entity); + return ret; +} + +void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx) +{ + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + media_entity_cleanup(&ctx->vdev.entity); +} diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index fdb40c85de9b..e81c8507893b 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1,9 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * TI CAL camera interface driver + * TI Camera Access Layer (CAL) - Driver * - * Copyright (c) 2015 Texas Instruments Inc. - * Benoit Parrot, + * Copyright (c) 2015-2020 Texas Instruments Inc. + * + * Authors: + * Benoit Parrot + * Laurent Pinchart */ #include @@ -11,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -27,319 +29,26 @@ #include #include #include -#include -#include #include -#include #include #include +#include "cal.h" #include "cal_regs.h" -#define CAL_MODULE_NAME "cal" - MODULE_DESCRIPTION("TI CAL driver"); MODULE_AUTHOR("Benoit Parrot, "); MODULE_LICENSE("GPL v2"); MODULE_VERSION("0.1.0"); -static unsigned video_nr = -1; -module_param(video_nr, uint, 0644); +int cal_video_nr = -1; +module_param_named(video_nr, cal_video_nr, uint, 0644); MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect"); -static unsigned debug; -module_param(debug, uint, 0644); +unsigned int cal_debug; +module_param_named(debug, cal_debug, uint, 0644); MODULE_PARM_DESC(debug, "activates debug info"); -#define cal_dbg(level, cal, fmt, arg...) \ - do { \ - if (debug >= (level)) \ - dev_printk(KERN_DEBUG, (cal)->dev, fmt, ##arg); \ - } while (0) -#define cal_info(cal, fmt, arg...) \ - dev_info((cal)->dev, fmt, ##arg) -#define cal_err(cal, fmt, arg...) \ - dev_err((cal)->dev, fmt, ##arg) - -#define ctx_dbg(level, ctx, fmt, arg...) \ - cal_dbg(level, (ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) -#define ctx_info(ctx, fmt, arg...) \ - cal_info((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) -#define ctx_err(ctx, fmt, arg...) \ - cal_err((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) - -#define phy_dbg(level, phy, fmt, arg...) \ - cal_dbg(level, (phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) -#define phy_info(phy, fmt, arg...) \ - cal_info((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) -#define phy_err(phy, fmt, arg...) \ - cal_err((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) - -#define CAL_NUM_CONTEXT 2 -#define CAL_NUM_CSI2_PORTS 2 - -#define MAX_WIDTH_BYTES (8192 * 8) -#define MAX_HEIGHT_LINES 16383 - -/* ------------------------------------------------------------------ - * Format Handling - * ------------------------------------------------------------------ - */ - -struct cal_fmt { - u32 fourcc; - u32 code; - /* Bits per pixel */ - u8 bpp; -}; - -static const struct cal_fmt cal_formats[] = { - { - .fourcc = V4L2_PIX_FMT_YUYV, - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_UYVY, - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_YVYU, - .code = MEDIA_BUS_FMT_YVYU8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_VYUY, - .code = MEDIA_BUS_FMT_VYUY8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ - .code = MEDIA_BUS_FMT_RGB565_2X8_BE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ - .code = MEDIA_BUS_FMT_RGB888_2X12_LE, - .bpp = 24, - }, { - .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ - .code = MEDIA_BUS_FMT_RGB888_2X12_BE, - .bpp = 24, - }, { - .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ - .code = MEDIA_BUS_FMT_ARGB8888_1X32, - .bpp = 32, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR8, - .code = MEDIA_BUS_FMT_SBGGR8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG8, - .code = MEDIA_BUS_FMT_SGBRG8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG8, - .code = MEDIA_BUS_FMT_SGRBG8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB8, - .code = MEDIA_BUS_FMT_SRGGB8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG10, - .code = MEDIA_BUS_FMT_SGBRG10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG10, - .code = MEDIA_BUS_FMT_SGRBG10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB10, - .code = MEDIA_BUS_FMT_SRGGB10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR12, - .code = MEDIA_BUS_FMT_SBGGR12_1X12, - .bpp = 12, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG12, - .code = MEDIA_BUS_FMT_SGBRG12_1X12, - .bpp = 12, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG12, - .code = MEDIA_BUS_FMT_SGRBG12_1X12, - .bpp = 12, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB12, - .code = MEDIA_BUS_FMT_SRGGB12_1X12, - .bpp = 12, - }, -}; - -/* Print Four-character-code (FOURCC) */ -static char *fourcc_to_str(u32 fmt) -{ - static char code[5]; - - code[0] = (unsigned char)(fmt & 0xff); - code[1] = (unsigned char)((fmt >> 8) & 0xff); - code[2] = (unsigned char)((fmt >> 16) & 0xff); - code[3] = (unsigned char)((fmt >> 24) & 0xff); - code[4] = '\0'; - - return code; -} - -/* ------------------------------------------------------------------ - * Driver Structures - * ------------------------------------------------------------------ - */ - -/* buffer for one video frame */ -struct cal_buffer { - /* common v4l buffer stuff -- must be first */ - struct vb2_v4l2_buffer vb; - struct list_head list; -}; - -struct cal_dmaqueue { - struct list_head active; -}; - -/* CTRL_CORE_CAMERRX_CONTROL register field id */ -enum cal_camerarx_field { - F_CTRLCLKEN, - F_CAMMODE, - F_LANEENABLE, - F_CSI_MODE, - - F_MAX_FIELDS, -}; - -struct cal_camerarx_data { - struct { - unsigned int lsb; - unsigned int msb; - } fields[F_MAX_FIELDS]; - unsigned int num_lanes; -}; - -struct cal_data { - const struct cal_camerarx_data *camerarx; - unsigned int num_csi2_phy; - unsigned int flags; -}; - -/* - * The Camera Adaptation Layer (CAL) module is paired with one or more complex - * I/O PHYs (CAMERARX). It contains multiple instances of CSI-2, processing and - * DMA contexts. - * - * The cal_dev structure represents the whole subsystem, including the CAL and - * the CAMERARX instances. Instances of struct cal_dev are named cal through the - * driver. - * - * The cal_camerarx structure represents one CAMERARX instance. Instances of - * cal_camerarx are named phy through the driver. - * - * The cal_ctx structure represents the combination of one CSI-2 context, one - * processing context and one DMA context. Instance of struct cal_ctx are named - * ctx through the driver. - */ - -struct cal_camerarx { - void __iomem *base; - struct resource *res; - struct device *dev; - struct regmap_field *fields[F_MAX_FIELDS]; - - struct cal_dev *cal; - unsigned int instance; - - struct v4l2_fwnode_endpoint endpoint; - struct device_node *sensor_node; - struct v4l2_subdev *sensor; -}; - -struct cal_dev { - struct clk *fclk; - int irq; - void __iomem *base; - struct resource *res; - struct device *dev; - - const struct cal_data *data; - u32 revision; - - /* Control Module handle */ - struct regmap *syscon_camerrx; - u32 syscon_camerrx_offset; - - /* Camera Core Module handle */ - struct cal_camerarx *phy[CAL_NUM_CSI2_PORTS]; - - struct cal_ctx *ctx[CAL_NUM_CONTEXT]; - - struct media_device mdev; - struct v4l2_device v4l2_dev; - struct v4l2_async_notifier notifier; -}; - -/* - * There is one cal_ctx structure for each camera core context. - */ -struct cal_ctx { - struct v4l2_ctrl_handler ctrl_handler; - struct video_device vdev; - struct media_pad pad; - - struct cal_dev *cal; - struct cal_camerarx *phy; - - /* v4l2_ioctl mutex */ - struct mutex mutex; - /* v4l2 buffers lock */ - spinlock_t slock; - - struct cal_dmaqueue vidq; - - /* video capture */ - const struct cal_fmt *fmt; - /* Used to store current pixel format */ - struct v4l2_format v_fmt; - /* Used to store current mbus frame format */ - struct v4l2_mbus_framefmt m_fmt; - - /* Current subdev enumerated format */ - const struct cal_fmt **active_fmt; - unsigned int num_active_fmt; - - unsigned int sequence; - struct vb2_queue vb_vidq; - unsigned int index; - unsigned int cport; - - /* Pointer pointing to current v4l2_buffer */ - struct cal_buffer *cur_frm; - /* Pointer pointing to next v4l2_buffer */ - struct cal_buffer *next_frm; - - bool dma_act; -}; - /* ------------------------------------------------------------------ * Platform Data * ------------------------------------------------------------------ @@ -451,7 +160,7 @@ static inline void set_field(u32 *valp, u32 field, u32 mask) *valp = val; } -static void cal_quickdump_regs(struct cal_dev *cal) +void cal_quickdump_regs(struct cal_dev *cal) { unsigned int i; @@ -668,8 +377,7 @@ static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) phy_err(phy, "Timeout waiting for stop state\n"); } -static int cal_camerarx_start(struct cal_camerarx *phy, - const struct cal_fmt *fmt) +int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) { s64 external_rate; u32 sscounter; @@ -796,7 +504,7 @@ static int cal_camerarx_start(struct cal_camerarx *phy, return 0; } -static void cal_camerarx_stop(struct cal_camerarx *phy) +void cal_camerarx_stop(struct cal_camerarx *phy) { unsigned int i; int ret; @@ -865,7 +573,7 @@ static void cal_camerarx_i913_errata(struct cal_camerarx *phy) /* * Enable the expected IRQ sources */ -static void cal_camerarx_enable_irqs(struct cal_camerarx *phy) +void cal_camerarx_enable_irqs(struct cal_camerarx *phy) { u32 val; @@ -896,7 +604,7 @@ static void cal_camerarx_enable_irqs(struct cal_camerarx *phy) reg_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); } -static void cal_camerarx_disable_irqs(struct cal_camerarx *phy) +void cal_camerarx_disable_irqs(struct cal_camerarx *phy) { u32 val; @@ -918,14 +626,14 @@ static void cal_camerarx_disable_irqs(struct cal_camerarx *phy) reg_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0); } -static void cal_camerarx_ppi_enable(struct cal_camerarx *phy) +void cal_camerarx_ppi_enable(struct cal_camerarx *phy) { reg_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), BIT(3)); reg_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } -static void cal_camerarx_ppi_disable(struct cal_camerarx *phy) +void cal_camerarx_ppi_disable(struct cal_camerarx *phy) { reg_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); @@ -1145,7 +853,7 @@ static int cal_camerarx_init_regmap(struct cal_dev *cal) * ------------------------------------------------------------------ */ -static void cal_ctx_csi2_config(struct cal_ctx *ctx) +void cal_ctx_csi2_config(struct cal_ctx *ctx) { u32 val; @@ -1170,7 +878,7 @@ static void cal_ctx_csi2_config(struct cal_ctx *ctx) reg_read(ctx->cal, CAL_CSI2_CTX0(ctx->index))); } -static void cal_ctx_pix_proc_config(struct cal_ctx *ctx) +void cal_ctx_pix_proc_config(struct cal_ctx *ctx) { u32 val, extract, pack; @@ -1221,8 +929,8 @@ static void cal_ctx_pix_proc_config(struct cal_ctx *ctx) reg_read(ctx->cal, CAL_PIX_PROC(ctx->index))); } -static void cal_ctx_wr_dma_config(struct cal_ctx *ctx, - unsigned int width, unsigned int height) +void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, + unsigned int height) { u32 val; @@ -1275,7 +983,7 @@ static void cal_ctx_wr_dma_config(struct cal_ctx *ctx, ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", reg_read(ctx->cal, CAL_CTRL)); } -static void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) +void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) { reg_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), dmaaddr); } @@ -1388,753 +1096,6 @@ static irqreturn_t cal_irq(int irq_cal, void *data) return IRQ_HANDLED; } -/* ------------------------------------------------------------------ - * V4L2 Video IOCTLs - * ------------------------------------------------------------------ - */ - -static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx, - u32 pixelformat) -{ - const struct cal_fmt *fmt; - unsigned int k; - - for (k = 0; k < ctx->num_active_fmt; k++) { - fmt = ctx->active_fmt[k]; - if (fmt->fourcc == pixelformat) - return fmt; - } - - return NULL; -} - -static const struct cal_fmt *find_format_by_code(struct cal_ctx *ctx, - u32 code) -{ - const struct cal_fmt *fmt; - unsigned int k; - - for (k = 0; k < ctx->num_active_fmt; k++) { - fmt = ctx->active_fmt[k]; - if (fmt->code == code) - return fmt; - } - - return NULL; -} - -static int cal_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct cal_ctx *ctx = video_drvdata(file); - - strscpy(cap->driver, CAL_MODULE_NAME, sizeof(cap->driver)); - strscpy(cap->card, CAL_MODULE_NAME, sizeof(cap->card)); - - snprintf(cap->bus_info, sizeof(cap->bus_info), - "platform:%s", dev_name(ctx->cal->dev)); - return 0; -} - -static int cal_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; - - if (f->index >= ctx->num_active_fmt) - return -EINVAL; - - fmt = ctx->active_fmt[f->index]; - - f->pixelformat = fmt->fourcc; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - return 0; -} - -static int __subdev_get_format(struct cal_ctx *ctx, - struct v4l2_mbus_framefmt *fmt) -{ - struct v4l2_subdev_format sd_fmt; - struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; - int ret; - - sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; - sd_fmt.pad = 0; - - ret = v4l2_subdev_call(ctx->phy->sensor, pad, get_fmt, NULL, &sd_fmt); - if (ret) - return ret; - - *fmt = *mbus_fmt; - - ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, - fmt->width, fmt->height, fmt->code); - - return 0; -} - -static int __subdev_set_format(struct cal_ctx *ctx, - struct v4l2_mbus_framefmt *fmt) -{ - struct v4l2_subdev_format sd_fmt; - struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; - int ret; - - sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; - sd_fmt.pad = 0; - *mbus_fmt = *fmt; - - ret = v4l2_subdev_call(ctx->phy->sensor, pad, set_fmt, NULL, &sd_fmt); - if (ret) - return ret; - - ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, - fmt->width, fmt->height, fmt->code); - - return 0; -} - -static int cal_calc_format_size(struct cal_ctx *ctx, - const struct cal_fmt *fmt, - struct v4l2_format *f) -{ - u32 bpl, max_width; - - if (!fmt) { - ctx_dbg(3, ctx, "No cal_fmt provided!\n"); - return -EINVAL; - } - - /* - * Maximum width is bound by the DMA max width in bytes. - * We need to recalculate the actual maxi width depending on the - * number of bytes per pixels required. - */ - max_width = MAX_WIDTH_BYTES / (ALIGN(fmt->bpp, 8) >> 3); - v4l_bound_align_image(&f->fmt.pix.width, 48, max_width, 2, - &f->fmt.pix.height, 32, MAX_HEIGHT_LINES, 0, 0); - - bpl = (f->fmt.pix.width * ALIGN(fmt->bpp, 8)) >> 3; - f->fmt.pix.bytesperline = ALIGN(bpl, 16); - - f->fmt.pix.sizeimage = f->fmt.pix.height * - f->fmt.pix.bytesperline; - - ctx_dbg(3, ctx, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n", - __func__, fourcc_to_str(f->fmt.pix.pixelformat), - f->fmt.pix.width, f->fmt.pix.height, - f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); - - return 0; -} - -static int cal_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cal_ctx *ctx = video_drvdata(file); - - *f = ctx->v_fmt; - - return 0; -} - -static int cal_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; - struct v4l2_subdev_frame_size_enum fse; - int ret, found; - - fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); - if (!fmt) { - ctx_dbg(3, ctx, "Fourcc format (0x%08x) not found.\n", - f->fmt.pix.pixelformat); - - /* Just get the first one enumerated */ - fmt = ctx->active_fmt[0]; - f->fmt.pix.pixelformat = fmt->fourcc; - } - - f->fmt.pix.field = ctx->v_fmt.fmt.pix.field; - - /* check for/find a valid width/height */ - ret = 0; - found = false; - fse.pad = 0; - fse.code = fmt->code; - fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; - for (fse.index = 0; ; fse.index++) { - ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, - NULL, &fse); - if (ret) - break; - - if ((f->fmt.pix.width == fse.max_width) && - (f->fmt.pix.height == fse.max_height)) { - found = true; - break; - } else if ((f->fmt.pix.width >= fse.min_width) && - (f->fmt.pix.width <= fse.max_width) && - (f->fmt.pix.height >= fse.min_height) && - (f->fmt.pix.height <= fse.max_height)) { - found = true; - break; - } - } - - if (!found) { - /* use existing values as default */ - f->fmt.pix.width = ctx->v_fmt.fmt.pix.width; - f->fmt.pix.height = ctx->v_fmt.fmt.pix.height; - } - - /* - * Use current colorspace for now, it will get - * updated properly during s_fmt - */ - f->fmt.pix.colorspace = ctx->v_fmt.fmt.pix.colorspace; - return cal_calc_format_size(ctx, fmt, f); -} - -static int cal_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cal_ctx *ctx = video_drvdata(file); - struct vb2_queue *q = &ctx->vb_vidq; - const struct cal_fmt *fmt; - struct v4l2_mbus_framefmt mbus_fmt; - int ret; - - if (vb2_is_busy(q)) { - ctx_dbg(3, ctx, "%s device busy\n", __func__); - return -EBUSY; - } - - ret = cal_try_fmt_vid_cap(file, priv, f); - if (ret < 0) - return ret; - - fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); - - v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code); - - ret = __subdev_set_format(ctx, &mbus_fmt); - if (ret) - return ret; - - /* Just double check nothing has gone wrong */ - if (mbus_fmt.code != fmt->code) { - ctx_dbg(3, ctx, - "%s subdev changed format on us, this should not happen\n", - __func__); - return -EINVAL; - } - - v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); - ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; - cal_calc_format_size(ctx, fmt, &ctx->v_fmt); - ctx->fmt = fmt; - ctx->m_fmt = mbus_fmt; - *f = ctx->v_fmt; - - return 0; -} - -static int cal_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; - struct v4l2_subdev_frame_size_enum fse; - int ret; - - /* check for valid format */ - fmt = find_format_by_pix(ctx, fsize->pixel_format); - if (!fmt) { - ctx_dbg(3, ctx, "Invalid pixel code: %x\n", - fsize->pixel_format); - return -EINVAL; - } - - fse.index = fsize->index; - fse.pad = 0; - fse.code = fmt->code; - fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; - - ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, NULL, - &fse); - if (ret) - return ret; - - ctx_dbg(1, ctx, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", - __func__, fse.index, fse.code, fse.min_width, fse.max_width, - fse.min_height, fse.max_height); - - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = fse.max_width; - fsize->discrete.height = fse.max_height; - - return 0; -} - -static int cal_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - if (inp->index > 0) - return -EINVAL; - - inp->type = V4L2_INPUT_TYPE_CAMERA; - sprintf(inp->name, "Camera %u", inp->index); - return 0; -} - -static int cal_g_input(struct file *file, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int cal_s_input(struct file *file, void *priv, unsigned int i) -{ - return i > 0 ? -EINVAL : 0; -} - -/* timeperframe is arbitrary and continuous */ -static int cal_enum_frameintervals(struct file *file, void *priv, - struct v4l2_frmivalenum *fival) -{ - struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; - struct v4l2_subdev_frame_interval_enum fie = { - .index = fival->index, - .width = fival->width, - .height = fival->height, - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - int ret; - - fmt = find_format_by_pix(ctx, fival->pixel_format); - if (!fmt) - return -EINVAL; - - fie.code = fmt->code; - ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_interval, - NULL, &fie); - if (ret) - return ret; - fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; - fival->discrete = fie.interval; - - return 0; -} - -static const struct v4l2_file_operations cal_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .read = vb2_fop_read, - .poll = vb2_fop_poll, - .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ - .mmap = vb2_fop_mmap, -}; - -static const struct v4l2_ioctl_ops cal_ioctl_ops = { - .vidioc_querycap = cal_querycap, - .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap, - .vidioc_enum_framesizes = cal_enum_framesizes, - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_enum_input = cal_enum_input, - .vidioc_g_input = cal_g_input, - .vidioc_s_input = cal_s_input, - .vidioc_enum_frameintervals = cal_enum_frameintervals, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -/* ------------------------------------------------------------------ - * videobuf2 Operations - * ------------------------------------------------------------------ - */ - -static int cal_queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], struct device *alloc_devs[]) -{ - struct cal_ctx *ctx = vb2_get_drv_priv(vq); - unsigned int size = ctx->v_fmt.fmt.pix.sizeimage; - - if (vq->num_buffers + *nbuffers < 3) - *nbuffers = 3 - vq->num_buffers; - - if (*nplanes) { - if (sizes[0] < size) - return -EINVAL; - size = sizes[0]; - } - - *nplanes = 1; - sizes[0] = size; - - ctx_dbg(3, ctx, "nbuffers=%d, size=%d\n", *nbuffers, sizes[0]); - - return 0; -} - -static int cal_buffer_prepare(struct vb2_buffer *vb) -{ - struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct cal_buffer *buf = container_of(vb, struct cal_buffer, - vb.vb2_buf); - unsigned long size; - - if (WARN_ON(!ctx->fmt)) - return -EINVAL; - - size = ctx->v_fmt.fmt.pix.sizeimage; - if (vb2_plane_size(vb, 0) < size) { - ctx_err(ctx, - "data will not fit into plane (%lu < %lu)\n", - vb2_plane_size(vb, 0), size); - return -EINVAL; - } - - vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); - return 0; -} - -static void cal_buffer_queue(struct vb2_buffer *vb) -{ - struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct cal_buffer *buf = container_of(vb, struct cal_buffer, - vb.vb2_buf); - struct cal_dmaqueue *vidq = &ctx->vidq; - unsigned long flags; - - /* recheck locking */ - spin_lock_irqsave(&ctx->slock, flags); - list_add_tail(&buf->list, &vidq->active); - spin_unlock_irqrestore(&ctx->slock, flags); -} - -static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct cal_ctx *ctx = vb2_get_drv_priv(vq); - struct cal_dmaqueue *dma_q = &ctx->vidq; - struct cal_buffer *buf, *tmp; - unsigned long addr; - unsigned long flags; - int ret; - - spin_lock_irqsave(&ctx->slock, flags); - if (list_empty(&dma_q->active)) { - spin_unlock_irqrestore(&ctx->slock, flags); - ctx_dbg(3, ctx, "buffer queue is empty\n"); - return -EIO; - } - - buf = list_entry(dma_q->active.next, struct cal_buffer, list); - ctx->cur_frm = buf; - ctx->next_frm = buf; - list_del(&buf->list); - spin_unlock_irqrestore(&ctx->slock, flags); - - addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0); - ctx->sequence = 0; - - pm_runtime_get_sync(ctx->cal->dev); - - cal_ctx_csi2_config(ctx); - cal_ctx_pix_proc_config(ctx); - cal_ctx_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline, - ctx->v_fmt.fmt.pix.height); - - cal_camerarx_enable_irqs(ctx->phy); - - ret = cal_camerarx_start(ctx->phy, ctx->fmt); - if (ret) - goto err; - - cal_ctx_wr_dma_addr(ctx, addr); - cal_camerarx_ppi_enable(ctx->phy); - - if (debug >= 4) - cal_quickdump_regs(ctx->cal); - - return 0; - -err: - spin_lock_irqsave(&ctx->slock, flags); - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED); - ctx->cur_frm = NULL; - ctx->next_frm = NULL; - list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); - } - spin_unlock_irqrestore(&ctx->slock, flags); - return ret; -} - -static void cal_stop_streaming(struct vb2_queue *vq) -{ - struct cal_ctx *ctx = vb2_get_drv_priv(vq); - struct cal_dmaqueue *dma_q = &ctx->vidq; - struct cal_buffer *buf, *tmp; - unsigned long timeout; - unsigned long flags; - bool dma_act; - - cal_camerarx_ppi_disable(ctx->phy); - - /* wait for stream and dma to finish */ - dma_act = true; - timeout = jiffies + msecs_to_jiffies(500); - while (dma_act && time_before(jiffies, timeout)) { - msleep(50); - - spin_lock_irqsave(&ctx->slock, flags); - dma_act = ctx->dma_act; - spin_unlock_irqrestore(&ctx->slock, flags); - } - - if (dma_act) - ctx_err(ctx, "failed to disable dma cleanly\n"); - - cal_camerarx_disable_irqs(ctx->phy); - cal_camerarx_stop(ctx->phy); - - /* Release all active buffers */ - spin_lock_irqsave(&ctx->slock, flags); - list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } - - if (ctx->cur_frm == ctx->next_frm) { - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } else { - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); - vb2_buffer_done(&ctx->next_frm->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - } - ctx->cur_frm = NULL; - ctx->next_frm = NULL; - spin_unlock_irqrestore(&ctx->slock, flags); - - pm_runtime_put_sync(ctx->cal->dev); -} - -static const struct vb2_ops cal_video_qops = { - .queue_setup = cal_queue_setup, - .buf_prepare = cal_buffer_prepare, - .buf_queue = cal_buffer_queue, - .start_streaming = cal_start_streaming, - .stop_streaming = cal_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -/* ------------------------------------------------------------------ - * V4L2 Initialization and Registration - * ------------------------------------------------------------------ - */ - -static const struct video_device cal_videodev = { - .name = CAL_MODULE_NAME, - .fops = &cal_fops, - .ioctl_ops = &cal_ioctl_ops, - .minor = -1, - .release = video_device_release_empty, - .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE, -}; - -static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) -{ - struct v4l2_subdev_mbus_code_enum mbus_code; - struct v4l2_mbus_framefmt mbus_fmt; - const struct cal_fmt *fmt; - unsigned int i, j, k; - int ret = 0; - - /* Enumerate sub device formats and enable all matching local formats */ - ctx->active_fmt = devm_kcalloc(ctx->cal->dev, ARRAY_SIZE(cal_formats), - sizeof(*ctx->active_fmt), GFP_KERNEL); - if (!ctx->active_fmt) - return -ENOMEM; - - ctx->num_active_fmt = 0; - - for (j = 0, i = 0; ret != -EINVAL; ++j) { - - memset(&mbus_code, 0, sizeof(mbus_code)); - mbus_code.index = j; - mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_mbus_code, - NULL, &mbus_code); - if (ret) - continue; - - ctx_dbg(2, ctx, - "subdev %s: code: %04x idx: %u\n", - ctx->phy->sensor->name, mbus_code.code, j); - - for (k = 0; k < ARRAY_SIZE(cal_formats); k++) { - const struct cal_fmt *fmt = &cal_formats[k]; - - if (mbus_code.code == fmt->code) { - ctx->active_fmt[i] = fmt; - ctx_dbg(2, ctx, - "matched fourcc: %s: code: %04x idx: %u\n", - fourcc_to_str(fmt->fourcc), - fmt->code, i); - ctx->num_active_fmt = ++i; - } - } - } - - if (i == 0) { - ctx_err(ctx, "No suitable format reported by subdev %s\n", - ctx->phy->sensor->name); - return -EINVAL; - } - - ret = __subdev_get_format(ctx, &mbus_fmt); - if (ret) - return ret; - - fmt = find_format_by_code(ctx, mbus_fmt.code); - if (!fmt) { - ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n", - mbus_fmt.code); - return -EINVAL; - } - - /* Save current subdev format */ - v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); - ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; - cal_calc_format_size(ctx, fmt, &ctx->v_fmt); - ctx->fmt = fmt; - ctx->m_fmt = mbus_fmt; - - return 0; -} - -static int cal_ctx_v4l2_register(struct cal_ctx *ctx) -{ - struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; - struct video_device *vfd = &ctx->vdev; - int ret; - - ret = cal_ctx_v4l2_init_formats(ctx); - if (ret) - return ret; - - ret = v4l2_ctrl_add_handler(hdl, ctx->phy->sensor->ctrl_handler, NULL, - true); - if (ret < 0) { - ctx_err(ctx, "Failed to add sensor ctrl handler\n"); - return ret; - } - - ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr); - if (ret < 0) { - ctx_err(ctx, "Failed to register video device\n"); - return ret; - } - - ctx_info(ctx, "V4L2 device registered as %s\n", - video_device_node_name(vfd)); - - return 0; -} - -static void cal_ctx_v4l2_unregister(struct cal_ctx *ctx) -{ - ctx_dbg(1, ctx, "unregistering %s\n", - video_device_node_name(&ctx->vdev)); - - video_unregister_device(&ctx->vdev); -} - -static int cal_ctx_v4l2_init(struct cal_ctx *ctx) -{ - struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; - struct video_device *vfd = &ctx->vdev; - struct vb2_queue *q = &ctx->vb_vidq; - int ret; - - INIT_LIST_HEAD(&ctx->vidq.active); - spin_lock_init(&ctx->slock); - mutex_init(&ctx->mutex); - - /* Initialize the vb2 queue. */ - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; - q->drv_priv = ctx; - q->buf_struct_size = sizeof(struct cal_buffer); - q->ops = &cal_video_qops; - q->mem_ops = &vb2_dma_contig_memops; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->lock = &ctx->mutex; - q->min_buffers_needed = 3; - q->dev = ctx->cal->dev; - - ret = vb2_queue_init(q); - if (ret) - return ret; - - /* Initialize the video device and media entity. */ - *vfd = cal_videodev; - vfd->v4l2_dev = &ctx->cal->v4l2_dev; - vfd->queue = q; - snprintf(vfd->name, sizeof(vfd->name), "CAL output %u", ctx->index); - vfd->lock = &ctx->mutex; - video_set_drvdata(vfd, ctx); - - ctx->pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_pads_init(&vfd->entity, 1, &ctx->pad); - if (ret < 0) - return ret; - - /* Initialize the control handler. */ - ret = v4l2_ctrl_handler_init(hdl, 11); - if (ret < 0) { - ctx_err(ctx, "Failed to init ctrl handler\n"); - goto error; - } - - vfd->ctrl_handler = hdl; - - return 0; - -error: - media_entity_cleanup(&vfd->entity); - return ret; -} - -static void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx) -{ - v4l2_ctrl_handler_free(&ctx->ctrl_handler); - media_entity_cleanup(&ctx->vdev.entity); -} - /* ------------------------------------------------------------------ * Asynchronous V4L2 subdev binding * ------------------------------------------------------------------ diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h new file mode 100644 index 000000000000..383fa9295048 --- /dev/null +++ b/drivers/media/platform/ti-vpe/cal.h @@ -0,0 +1,226 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * TI Camera Access Layer (CAL) + * + * Copyright (c) 2015-2020 Texas Instruments Inc. + * + * Authors: + * Benoit Parrot + * Laurent Pinchart + */ +#ifndef __TI_CAL_H__ +#define __TI_CAL_H__ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define CAL_MODULE_NAME "cal" +#define CAL_NUM_CONTEXT 2 +#define CAL_NUM_CSI2_PORTS 2 + +#define MAX_WIDTH_BYTES (8192 * 8) +#define MAX_HEIGHT_LINES 16383 + +struct device; +struct device_node; +struct resource; +struct regmap; +struct regmap_fied; +struct v4l2_subdev; + +/* CTRL_CORE_CAMERRX_CONTROL register field id */ +enum cal_camerarx_field { + F_CTRLCLKEN, + F_CAMMODE, + F_LANEENABLE, + F_CSI_MODE, + F_MAX_FIELDS, +}; + +struct cal_fmt { + u32 fourcc; + u32 code; + /* Bits per pixel */ + u8 bpp; +}; + +/* buffer for one video frame */ +struct cal_buffer { + /* common v4l buffer stuff -- must be first */ + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +struct cal_dmaqueue { + struct list_head active; +}; + +struct cal_camerarx_data { + struct { + unsigned int lsb; + unsigned int msb; + } fields[F_MAX_FIELDS]; + unsigned int num_lanes; +}; + +struct cal_data { + const struct cal_camerarx_data *camerarx; + unsigned int num_csi2_phy; + unsigned int flags; +}; + +/* + * The Camera Adaptation Layer (CAL) module is paired with one or more complex + * I/O PHYs (CAMERARX). It contains multiple instances of CSI-2, processing and + * DMA contexts. + * + * The cal_dev structure represents the whole subsystem, including the CAL and + * the CAMERARX instances. Instances of struct cal_dev are named cal through the + * driver. + * + * The cal_camerarx structure represents one CAMERARX instance. Instances of + * cal_camerarx are named phy through the driver. + * + * The cal_ctx structure represents the combination of one CSI-2 context, one + * processing context and one DMA context. Instance of struct cal_ctx are named + * ctx through the driver. + */ + +struct cal_camerarx { + void __iomem *base; + struct resource *res; + struct device *dev; + struct regmap_field *fields[F_MAX_FIELDS]; + + struct cal_dev *cal; + unsigned int instance; + + struct v4l2_fwnode_endpoint endpoint; + struct device_node *sensor_node; + struct v4l2_subdev *sensor; +}; + +struct cal_dev { + struct clk *fclk; + int irq; + void __iomem *base; + struct resource *res; + struct device *dev; + + const struct cal_data *data; + u32 revision; + + /* Control Module handle */ + struct regmap *syscon_camerrx; + u32 syscon_camerrx_offset; + + /* Camera Core Module handle */ + struct cal_camerarx *phy[CAL_NUM_CSI2_PORTS]; + + struct cal_ctx *ctx[CAL_NUM_CONTEXT]; + + struct media_device mdev; + struct v4l2_device v4l2_dev; + struct v4l2_async_notifier notifier; +}; + +/* + * There is one cal_ctx structure for each camera core context. + */ +struct cal_ctx { + struct v4l2_ctrl_handler ctrl_handler; + struct video_device vdev; + struct media_pad pad; + + struct cal_dev *cal; + struct cal_camerarx *phy; + + /* v4l2_ioctl mutex */ + struct mutex mutex; + /* v4l2 buffers lock */ + spinlock_t slock; + + struct cal_dmaqueue vidq; + + /* video capture */ + const struct cal_fmt *fmt; + /* Used to store current pixel format */ + struct v4l2_format v_fmt; + /* Used to store current mbus frame format */ + struct v4l2_mbus_framefmt m_fmt; + + /* Current subdev enumerated format */ + const struct cal_fmt **active_fmt; + unsigned int num_active_fmt; + + unsigned int sequence; + struct vb2_queue vb_vidq; + unsigned int index; + unsigned int cport; + + /* Pointer pointing to current v4l2_buffer */ + struct cal_buffer *cur_frm; + /* Pointer pointing to next v4l2_buffer */ + struct cal_buffer *next_frm; + + bool dma_act; +}; + +extern unsigned int cal_debug; +extern int cal_video_nr; + +#define cal_dbg(level, cal, fmt, arg...) \ + do { \ + if (cal_debug >= (level)) \ + dev_printk(KERN_DEBUG, (cal)->dev, fmt, ##arg); \ + } while (0) +#define cal_info(cal, fmt, arg...) \ + dev_info((cal)->dev, fmt, ##arg) +#define cal_err(cal, fmt, arg...) \ + dev_err((cal)->dev, fmt, ##arg) + +#define ctx_dbg(level, ctx, fmt, arg...) \ + cal_dbg(level, (ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) +#define ctx_info(ctx, fmt, arg...) \ + cal_info((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) +#define ctx_err(ctx, fmt, arg...) \ + cal_err((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) + +#define phy_dbg(level, phy, fmt, arg...) \ + cal_dbg(level, (phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) +#define phy_info(phy, fmt, arg...) \ + cal_info((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) +#define phy_err(phy, fmt, arg...) \ + cal_err((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) + +void cal_quickdump_regs(struct cal_dev *cal); + +int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt); +void cal_camerarx_stop(struct cal_camerarx *phy); +void cal_camerarx_enable_irqs(struct cal_camerarx *phy); +void cal_camerarx_disable_irqs(struct cal_camerarx *phy); +void cal_camerarx_ppi_enable(struct cal_camerarx *phy); +void cal_camerarx_ppi_disable(struct cal_camerarx *phy); + +void cal_ctx_csi2_config(struct cal_ctx *ctx); +void cal_ctx_pix_proc_config(struct cal_ctx *ctx); +void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, + unsigned int height); +void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr); + +int cal_ctx_v4l2_register(struct cal_ctx *ctx); +void cal_ctx_v4l2_unregister(struct cal_ctx *ctx); +int cal_ctx_v4l2_init(struct cal_ctx *ctx); +void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx); + +#endif /* __TI_CAL_H__ */ -- cgit v1.2.3 From d373018f3abda9d2446a059133cd6425f56b3b51 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:44 +0200 Subject: media: ti-vpe: cal: Move CAL I/O accessors to cal.h To prepare for the split of the camerarx code to a separate file, move the CAL I/O accessors to cal.h. This requires renaming the accessors with a cal_prefix, as the current names are too generic and prone to namespace clashes. The reg_read() and read_write() macros, that cover both CAL and CAMERARX register access, are split in two groups of inline functions, one for CAL access and one for CAMERARX access. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-video.c | 1 - drivers/media/platform/ti-vpe/cal.c | 282 ++++++++++++++---------------- drivers/media/platform/ti-vpe/cal.h | 36 ++++ 3 files changed, 169 insertions(+), 150 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index 82b32a2f9db5..df472a175e83 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -9,7 +9,6 @@ * Laurent Pinchart */ -#include #include #include #include diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index e81c8507893b..c2252ed36d68 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -9,11 +9,9 @@ * Laurent Pinchart */ -#include #include #include #include -#include #include #include #include @@ -133,33 +131,6 @@ static const struct cal_data am654_cal_data = { * ------------------------------------------------------------------ */ -#define reg_read(dev, offset) ioread32(dev->base + offset) -#define reg_write(dev, offset, val) iowrite32(val, dev->base + offset) - -static inline u32 reg_read_field(struct cal_dev *cal, u32 offset, u32 mask) -{ - return FIELD_GET(mask, reg_read(cal, offset)); -} - -static inline void reg_write_field(struct cal_dev *cal, u32 offset, u32 value, - u32 mask) -{ - u32 val = reg_read(cal, offset); - - val &= ~mask; - val |= FIELD_PREP(mask, value); - reg_write(cal, offset, val); -} - -static inline void set_field(u32 *valp, u32 field, u32 mask) -{ - u32 val = *valp; - - val &= ~mask; - val |= (field << __ffs(mask)) & mask; - *valp = val; -} - void cal_quickdump_regs(struct cal_dev *cal) { unsigned int i; @@ -189,6 +160,16 @@ void cal_quickdump_regs(struct cal_dev *cal) * ------------------------------------------------------------------ */ +static inline u32 camerarx_read(struct cal_camerarx *phy, u32 offset) +{ + return ioread32(phy->base + offset); +} + +static inline void camerarx_write(struct cal_camerarx *phy, u32 offset, u32 val) +{ + iowrite32(val, phy->base + offset); +} + static s64 cal_camerarx_get_external_rate(struct cal_camerarx *phy) { struct v4l2_ctrl *ctrl; @@ -209,15 +190,15 @@ static s64 cal_camerarx_get_external_rate(struct cal_camerarx *phy) static void cal_camerarx_lane_config(struct cal_camerarx *phy) { - u32 val = reg_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)); + u32 val = cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)); u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = &phy->endpoint.bus.mipi_csi2; int lane; - set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); - set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask); + cal_set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); + cal_set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask); for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) { /* * Every lane are one nibble apart starting with the @@ -225,12 +206,12 @@ static void cal_camerarx_lane_config(struct cal_camerarx *phy) */ lane_mask <<= 4; polarity_mask <<= 4; - set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask); - set_field(&val, mipi_csi2->lane_polarities[lane + 1], - polarity_mask); + cal_set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask); + cal_set_field(&val, mipi_csi2->lane_polarities[lane + 1], + polarity_mask); } - reg_write(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), val); + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), val); phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", phy->instance, val); } @@ -291,23 +272,24 @@ static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate, ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; phy_dbg(1, phy, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); - reg0 = reg_read(phy, CAL_CSI2_PHY_REG0); - set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, - CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); - set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); - set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); + reg0 = camerarx_read(phy, CAL_CSI2_PHY_REG0); + cal_set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, + CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); + cal_set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); + cal_set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); phy_dbg(1, phy, "CSI2_%d_REG0 = 0x%08x\n", phy->instance, reg0); - reg_write(phy, CAL_CSI2_PHY_REG0, reg0); + camerarx_write(phy, CAL_CSI2_PHY_REG0, reg0); - reg1 = reg_read(phy, CAL_CSI2_PHY_REG1); - set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); - set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); - set_field(®1, TCLK_MISS, CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); - set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); + reg1 = camerarx_read(phy, CAL_CSI2_PHY_REG1); + cal_set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); + cal_set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); + cal_set_field(®1, TCLK_MISS, + CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); + cal_set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x\n", phy->instance, reg1); - reg_write(phy, CAL_CSI2_PHY_REG1, reg1); + camerarx_write(phy, CAL_CSI2_PHY_REG1, reg1); } static void cal_camerarx_power(struct cal_camerarx *phy, bool enable) @@ -318,13 +300,13 @@ static void cal_camerarx_power(struct cal_camerarx *phy, bool enable) target_state = enable ? CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON : CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF; - reg_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), target_state, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK); for (i = 0; i < 10; i++) { u32 current_state; - current_state = reg_read_field(phy->cal, + current_state = cal_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK); @@ -345,7 +327,7 @@ static void cal_camerarx_wait_reset(struct cal_camerarx *phy) timeout = jiffies + msecs_to_jiffies(750); while (time_before(jiffies, timeout)) { - if (reg_read_field(phy->cal, + if (cal_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) @@ -353,7 +335,7 @@ static void cal_camerarx_wait_reset(struct cal_camerarx *phy) usleep_range(500, 5000); } - if (reg_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + if (cal_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) != CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) phy_err(phy, "Timeout waiting for Complex IO reset done\n"); @@ -365,14 +347,14 @@ static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) timeout = jiffies + msecs_to_jiffies(750); while (time_before(jiffies, timeout)) { - if (reg_read_field(phy->cal, + if (cal_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance), CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0) break; usleep_range(500, 5000); } - if (reg_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance), + if (cal_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance), CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0) phy_err(phy, "Timeout waiting for stop state\n"); } @@ -421,15 +403,15 @@ int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) * at this point, as it requires the external sensor to send the * CSI-2 HS clock. */ - reg_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n", phy->instance, - reg_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance))); + cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance))); /* Dummy read to allow SCP reset to complete. */ - reg_read(phy, CAL_CSI2_PHY_REG0); + camerarx_read(phy, CAL_CSI2_PHY_REG0); /* Program the PHY timing parameters. */ cal_camerarx_config(phy, external_rate, fmt); @@ -446,21 +428,22 @@ int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) */ sscounter = DIV_ROUND_UP(clk_get_rate(phy->cal->fclk), 10000 * 16 * 4); - val = reg_read(phy->cal, CAL_CSI2_TIMING(phy->instance)); - set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK); - set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK); - set_field(&val, sscounter, CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK); - reg_write(phy->cal, CAL_CSI2_TIMING(phy->instance), val); + val = cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance)); + cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK); + cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK); + cal_set_field(&val, sscounter, + CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK); + cal_write(phy->cal, CAL_CSI2_TIMING(phy->instance), val); phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n", phy->instance, - reg_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); + cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); /* Assert the FORCERXMODE signal. */ - reg_write_field(phy->cal, CAL_CSI2_TIMING(phy->instance), + cal_write_field(phy->cal, CAL_CSI2_TIMING(phy->instance), 1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK); phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n", phy->instance, - reg_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); + cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); /* * c. Connect pull-down on CSI-2 PHY link (using pad control). @@ -492,7 +475,7 @@ int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) cal_camerarx_wait_stop_state(phy); phy_dbg(1, phy, "CSI2_%u_REG1 = 0x%08x (bits 31-28 should be set)\n", - phy->instance, reg_read(phy, CAL_CSI2_PHY_REG1)); + phy->instance, camerarx_read(phy, CAL_CSI2_PHY_REG1)); /* * g. Disable pull-down on CSI-2 PHY link (using pad control). @@ -512,13 +495,13 @@ void cal_camerarx_stop(struct cal_camerarx *phy) cal_camerarx_power(phy, false); /* Assert Complex IO Reset */ - reg_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); /* Wait for power down completion */ for (i = 0; i < 10; i++) { - if (reg_read_field(phy->cal, + if (cal_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING) @@ -527,7 +510,7 @@ void cal_camerarx_stop(struct cal_camerarx *phy) } phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n", phy->instance, - reg_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)), i, + cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)), i, (i >= 10) ? "(timeout)" : ""); /* Disable the phy */ @@ -562,12 +545,12 @@ void cal_camerarx_stop(struct cal_camerarx *phy) */ static void cal_camerarx_i913_errata(struct cal_camerarx *phy) { - u32 reg10 = reg_read(phy, CAL_CSI2_PHY_REG10); + u32 reg10 = camerarx_read(phy, CAL_CSI2_PHY_REG10); - set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); + cal_set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); phy_dbg(1, phy, "CSI2_%d_REG10 = 0x%08x\n", phy->instance, reg10); - reg_write(phy, CAL_CSI2_PHY_REG10, reg10); + camerarx_write(phy, CAL_CSI2_PHY_REG10, reg10); } /* @@ -584,24 +567,24 @@ void cal_camerarx_enable_irqs(struct cal_camerarx *phy) CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; /* Enable CIO error irqs */ - reg_write(phy->cal, CAL_HL_IRQENABLE_SET(0), + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_CIO_MASK(phy->instance)); - reg_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), cio_err_mask); /* Always enable OCPO error */ - reg_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); /* Enable IRQ_WDMA_END 0/1 */ val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - reg_write(phy->cal, CAL_HL_IRQENABLE_SET(1), val); + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(1), val); /* Enable IRQ_WDMA_START 0/1 */ val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - reg_write(phy->cal, CAL_HL_IRQENABLE_SET(2), val); + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(2), val); /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); + cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); } void cal_camerarx_disable_irqs(struct cal_camerarx *phy) @@ -609,33 +592,33 @@ void cal_camerarx_disable_irqs(struct cal_camerarx *phy) u32 val; /* Disable CIO error irqs */ - reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), + cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), CAL_HL_IRQ_CIO_MASK(phy->instance)); - reg_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0); /* Disable IRQ_WDMA_END 0/1 */ val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(1), val); + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(1), val); /* Disable IRQ_WDMA_START 0/1 */ val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(2), val); + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(2), val); /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0); + cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0); } void cal_camerarx_ppi_enable(struct cal_camerarx *phy) { - reg_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), BIT(3)); - reg_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), + cal_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), BIT(3)); + cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } void cal_camerarx_ppi_disable(struct cal_camerarx *phy) { - reg_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), + cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } @@ -857,8 +840,8 @@ void cal_ctx_csi2_config(struct cal_ctx *ctx) { u32 val; - val = reg_read(ctx->cal, CAL_CSI2_CTX0(ctx->index)); - set_field(&val, ctx->cport, CAL_CSI2_CTX_CPORT_MASK); + val = cal_read(ctx->cal, CAL_CSI2_CTX0(ctx->index)); + cal_set_field(&val, ctx->cport, CAL_CSI2_CTX_CPORT_MASK); /* * DT type: MIPI CSI-2 Specs * 0x1: All - DT filter is disabled @@ -867,15 +850,15 @@ void cal_ctx_csi2_config(struct cal_ctx *ctx) * 0x2A: RAW8 1 pixel = 1 byte * 0x1E: YUV422 2 pixels = 4 bytes */ - set_field(&val, 0x1, CAL_CSI2_CTX_DT_MASK); - set_field(&val, 0, CAL_CSI2_CTX_VC_MASK); - set_field(&val, ctx->v_fmt.fmt.pix.height, CAL_CSI2_CTX_LINES_MASK); - set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK); - set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE, - CAL_CSI2_CTX_PACK_MODE_MASK); - reg_write(ctx->cal, CAL_CSI2_CTX0(ctx->index), val); + cal_set_field(&val, 0x1, CAL_CSI2_CTX_DT_MASK); + cal_set_field(&val, 0, CAL_CSI2_CTX_VC_MASK); + cal_set_field(&val, ctx->v_fmt.fmt.pix.height, CAL_CSI2_CTX_LINES_MASK); + cal_set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK); + cal_set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE, + CAL_CSI2_CTX_PACK_MODE_MASK); + cal_write(ctx->cal, CAL_CSI2_CTX0(ctx->index), val); ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->index, - reg_read(ctx->cal, CAL_CSI2_CTX0(ctx->index))); + cal_read(ctx->cal, CAL_CSI2_CTX0(ctx->index))); } void cal_ctx_pix_proc_config(struct cal_ctx *ctx) @@ -917,16 +900,16 @@ void cal_ctx_pix_proc_config(struct cal_ctx *ctx) break; } - val = reg_read(ctx->cal, CAL_PIX_PROC(ctx->index)); - set_field(&val, extract, CAL_PIX_PROC_EXTRACT_MASK); - set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK); - set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK); - set_field(&val, pack, CAL_PIX_PROC_PACK_MASK); - set_field(&val, ctx->cport, CAL_PIX_PROC_CPORT_MASK); - set_field(&val, 1, CAL_PIX_PROC_EN_MASK); - reg_write(ctx->cal, CAL_PIX_PROC(ctx->index), val); + val = cal_read(ctx->cal, CAL_PIX_PROC(ctx->index)); + cal_set_field(&val, extract, CAL_PIX_PROC_EXTRACT_MASK); + cal_set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK); + cal_set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK); + cal_set_field(&val, pack, CAL_PIX_PROC_PACK_MASK); + cal_set_field(&val, ctx->cport, CAL_PIX_PROC_CPORT_MASK); + cal_set_field(&val, 1, CAL_PIX_PROC_EN_MASK); + cal_write(ctx->cal, CAL_PIX_PROC(ctx->index), val); ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->index, - reg_read(ctx->cal, CAL_PIX_PROC(ctx->index))); + cal_read(ctx->cal, CAL_PIX_PROC(ctx->index))); } void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, @@ -934,58 +917,59 @@ void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, { u32 val; - val = reg_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index)); - set_field(&val, ctx->cport, CAL_WR_DMA_CTRL_CPORT_MASK); - set_field(&val, height, CAL_WR_DMA_CTRL_YSIZE_MASK); - set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT, - CAL_WR_DMA_CTRL_DTAG_MASK); - set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST, - CAL_WR_DMA_CTRL_MODE_MASK); - set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR, - CAL_WR_DMA_CTRL_PATTERN_MASK); - set_field(&val, 1, CAL_WR_DMA_CTRL_STALL_RD_MASK); - reg_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->index), val); + val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index)); + cal_set_field(&val, ctx->cport, CAL_WR_DMA_CTRL_CPORT_MASK); + cal_set_field(&val, height, CAL_WR_DMA_CTRL_YSIZE_MASK); + cal_set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT, + CAL_WR_DMA_CTRL_DTAG_MASK); + cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST, + CAL_WR_DMA_CTRL_MODE_MASK); + cal_set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR, + CAL_WR_DMA_CTRL_PATTERN_MASK); + cal_set_field(&val, 1, CAL_WR_DMA_CTRL_STALL_RD_MASK); + cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->index), val); ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->index, - reg_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index))); + cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index))); /* * width/16 not sure but giving it a whirl. * zero does not work right */ - reg_write_field(ctx->cal, + cal_write_field(ctx->cal, CAL_WR_DMA_OFST(ctx->index), (width / 16), CAL_WR_DMA_OFST_MASK); ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->index, - reg_read(ctx->cal, CAL_WR_DMA_OFST(ctx->index))); + cal_read(ctx->cal, CAL_WR_DMA_OFST(ctx->index))); - val = reg_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index)); + val = cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index)); /* 64 bit word means no skipping */ - set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK); + cal_set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK); /* * (width*8)/64 this should be size of an entire line * in 64bit word but 0 means all data until the end * is detected automagically */ - set_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK); - reg_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index), val); + cal_set_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK); + cal_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index), val); ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->index, - reg_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index))); - - val = reg_read(ctx->cal, CAL_CTRL); - set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, CAL_CTRL_BURSTSIZE_MASK); - set_field(&val, 0xF, CAL_CTRL_TAGCNT_MASK); - set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED, - CAL_CTRL_POSTED_WRITES_MASK); - set_field(&val, 0xFF, CAL_CTRL_MFLAGL_MASK); - set_field(&val, 0xFF, CAL_CTRL_MFLAGH_MASK); - reg_write(ctx->cal, CAL_CTRL, val); - ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", reg_read(ctx->cal, CAL_CTRL)); + cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index))); + + val = cal_read(ctx->cal, CAL_CTRL); + cal_set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, + CAL_CTRL_BURSTSIZE_MASK); + cal_set_field(&val, 0xF, CAL_CTRL_TAGCNT_MASK); + cal_set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED, + CAL_CTRL_POSTED_WRITES_MASK); + cal_set_field(&val, 0xFF, CAL_CTRL_MFLAGL_MASK); + cal_set_field(&val, 0xFF, CAL_CTRL_MFLAGH_MASK); + cal_write(ctx->cal, CAL_CTRL, val); + ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", cal_read(ctx->cal, CAL_CTRL)); } void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) { - reg_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), dmaaddr); + cal_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), dmaaddr); } /* ------------------------------------------------------------------ @@ -1024,36 +1008,36 @@ static irqreturn_t cal_irq(int irq_cal, void *data) struct cal_dmaqueue *dma_q; u32 status; - status = reg_read(cal, CAL_HL_IRQSTATUS(0)); + status = cal_read(cal, CAL_HL_IRQSTATUS(0)); if (status) { unsigned int i; - reg_write(cal, CAL_HL_IRQSTATUS(0), status); + cal_write(cal, CAL_HL_IRQSTATUS(0), status); if (status & CAL_HL_IRQ_OCPO_ERR_MASK) dev_err_ratelimited(cal->dev, "OCPO ERROR\n"); for (i = 0; i < CAL_NUM_CSI2_PORTS; ++i) { if (status & CAL_HL_IRQ_CIO_MASK(i)) { - u32 cio_stat = reg_read(cal, + u32 cio_stat = cal_read(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); dev_err_ratelimited(cal->dev, "CIO%u error: %#08x\n", i, cio_stat); - reg_write(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i), + cal_write(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i), cio_stat); } } } /* Check which DMA just finished */ - status = reg_read(cal, CAL_HL_IRQSTATUS(1)); + status = cal_read(cal, CAL_HL_IRQSTATUS(1)); if (status) { unsigned int i; /* Clear Interrupt status */ - reg_write(cal, CAL_HL_IRQSTATUS(1), status); + cal_write(cal, CAL_HL_IRQSTATUS(1), status); for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { if (status & CAL_HL_IRQ_MASK(i)) { @@ -1071,12 +1055,12 @@ static irqreturn_t cal_irq(int irq_cal, void *data) } /* Check which DMA just started */ - status = reg_read(cal, CAL_HL_IRQSTATUS(2)); + status = cal_read(cal, CAL_HL_IRQSTATUS(2)); if (status) { unsigned int i; /* Clear Interrupt status */ - reg_write(cal, CAL_HL_IRQSTATUS(2), status); + cal_write(cal, CAL_HL_IRQSTATUS(2), status); for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { if (status & CAL_HL_IRQ_MASK(i)) { @@ -1355,7 +1339,7 @@ static void cal_get_hwinfo(struct cal_dev *cal) { u32 hwinfo; - cal->revision = reg_read(cal, CAL_HL_REVISION); + cal->revision = cal_read(cal, CAL_HL_REVISION); switch (FIELD_GET(CAL_HL_REVISION_SCHEME_MASK, cal->revision)) { case CAL_HL_REVISION_SCHEME_H08: cal_dbg(3, cal, "CAL HW revision %lu.%lu.%lu (0x%08x)\n", @@ -1372,7 +1356,7 @@ static void cal_get_hwinfo(struct cal_dev *cal) break; } - hwinfo = reg_read(cal, CAL_HL_HWINFO); + hwinfo = cal_read(cal, CAL_HL_HWINFO); if (hwinfo != CAL_HL_HWINFO_VALUE) cal_info(cal, "CAL_HL_HWINFO = 0x%08x, expected 0x%08x\n", hwinfo, CAL_HL_HWINFO_VALUE); diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h index 383fa9295048..44f2836b6543 100644 --- a/drivers/media/platform/ti-vpe/cal.h +++ b/drivers/media/platform/ti-vpe/cal.h @@ -11,6 +11,8 @@ #ifndef __TI_CAL_H__ #define __TI_CAL_H__ +#include +#include #include #include #include @@ -203,6 +205,40 @@ extern int cal_video_nr; #define phy_err(phy, fmt, arg...) \ cal_err((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) +static inline u32 cal_read(struct cal_dev *cal, u32 offset) +{ + return ioread32(cal->base + offset); +} + +static inline void cal_write(struct cal_dev *cal, u32 offset, u32 val) +{ + iowrite32(val, cal->base + offset); +} + +static inline u32 cal_read_field(struct cal_dev *cal, u32 offset, u32 mask) +{ + return FIELD_GET(mask, cal_read(cal, offset)); +} + +static inline void cal_write_field(struct cal_dev *cal, u32 offset, u32 value, + u32 mask) +{ + u32 val = cal_read(cal, offset); + + val &= ~mask; + val |= FIELD_PREP(mask, value); + cal_write(cal, offset, val); +} + +static inline void cal_set_field(u32 *valp, u32 field, u32 mask) +{ + u32 val = *valp; + + val &= ~mask; + val |= (field << __ffs(mask)) & mask; + *valp = val; +} + void cal_quickdump_regs(struct cal_dev *cal); int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt); -- cgit v1.2.3 From af30d4f36ef35d23f1ab6c5e4f329c743fb3c360 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:45 +0200 Subject: media: ti-vpe: cal: Split CAMERARX handling to cal-camerarx.c Implementation of media controller centric device configuration will add code to the CAMERARX support section, which is already quite big. Move it to a separate file to make the code more manageable. No functional change is included. The cal_camerarx_init_regmap() function is kept in cal.c and renamed to cal_init_camerarx_regmap() as it's not specific to one CAMERARX instance, but related to the whole CAL device. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/Makefile | 2 +- drivers/media/platform/ti-vpe/cal-camerarx.c | 649 +++++++++++++++++++++++ drivers/media/platform/ti-vpe/cal.c | 739 +++------------------------ drivers/media/platform/ti-vpe/cal.h | 5 + 4 files changed, 713 insertions(+), 682 deletions(-) create mode 100644 drivers/media/platform/ti-vpe/cal-camerarx.c (limited to 'drivers/media') diff --git a/drivers/media/platform/ti-vpe/Makefile b/drivers/media/platform/ti-vpe/Makefile index a7bf8f2a1386..ad624056e039 100644 --- a/drivers/media/platform/ti-vpe/Makefile +++ b/drivers/media/platform/ti-vpe/Makefile @@ -13,4 +13,4 @@ ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG obj-$(CONFIG_VIDEO_TI_CAL) += ti-cal.o -ti-cal-y := cal.o cal-video.o +ti-cal-y := cal.o cal-camerarx.o cal-video.o diff --git a/drivers/media/platform/ti-vpe/cal-camerarx.c b/drivers/media/platform/ti-vpe/cal-camerarx.c new file mode 100644 index 000000000000..806cbf175d39 --- /dev/null +++ b/drivers/media/platform/ti-vpe/cal-camerarx.c @@ -0,0 +1,649 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI Camera Access Layer (CAL) - CAMERARX + * + * Copyright (c) 2015-2020 Texas Instruments Inc. + * + * Authors: + * Benoit Parrot + * Laurent Pinchart + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "cal.h" +#include "cal_regs.h" + +/* ------------------------------------------------------------------ + * I/O Register Accessors + * ------------------------------------------------------------------ + */ + +static inline u32 camerarx_read(struct cal_camerarx *phy, u32 offset) +{ + return ioread32(phy->base + offset); +} + +static inline void camerarx_write(struct cal_camerarx *phy, u32 offset, u32 val) +{ + iowrite32(val, phy->base + offset); +} + +/* ------------------------------------------------------------------ + * CAMERARX Management + * ------------------------------------------------------------------ + */ + +static s64 cal_camerarx_get_external_rate(struct cal_camerarx *phy) +{ + struct v4l2_ctrl *ctrl; + s64 rate; + + ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); + if (!ctrl) { + phy_err(phy, "no pixel rate control in subdev: %s\n", + phy->sensor->name); + return -EPIPE; + } + + rate = v4l2_ctrl_g_ctrl_int64(ctrl); + phy_dbg(3, phy, "sensor Pixel Rate: %llu\n", rate); + + return rate; +} + +static void cal_camerarx_lane_config(struct cal_camerarx *phy) +{ + u32 val = cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)); + u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; + u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; + struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = + &phy->endpoint.bus.mipi_csi2; + int lane; + + cal_set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); + cal_set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask); + for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) { + /* + * Every lane are one nibble apart starting with the + * clock followed by the data lanes so shift masks by 4. + */ + lane_mask <<= 4; + polarity_mask <<= 4; + cal_set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask); + cal_set_field(&val, mipi_csi2->lane_polarities[lane + 1], + polarity_mask); + } + + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), val); + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", + phy->instance, val); +} + +static void cal_camerarx_enable(struct cal_camerarx *phy) +{ + u32 num_lanes = phy->cal->data->camerarx[phy->instance].num_lanes; + + regmap_field_write(phy->fields[F_CAMMODE], 0); + /* Always enable all lanes at the phy control level */ + regmap_field_write(phy->fields[F_LANEENABLE], (1 << num_lanes) - 1); + /* F_CSI_MODE is not present on every architecture */ + if (phy->fields[F_CSI_MODE]) + regmap_field_write(phy->fields[F_CSI_MODE], 1); + regmap_field_write(phy->fields[F_CTRLCLKEN], 1); +} + +void cal_camerarx_disable(struct cal_camerarx *phy) +{ + regmap_field_write(phy->fields[F_CTRLCLKEN], 0); +} + +/* + * TCLK values are OK at their reset values + */ +#define TCLK_TERM 0 +#define TCLK_MISS 1 +#define TCLK_SETTLE 14 + +static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate, + const struct cal_fmt *fmt) +{ + unsigned int reg0, reg1; + unsigned int ths_term, ths_settle; + unsigned int csi2_ddrclk_khz; + struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = + &phy->endpoint.bus.mipi_csi2; + u32 num_lanes = mipi_csi2->num_data_lanes; + + /* DPHY timing configuration */ + + /* + * CSI-2 is DDR and we only count used lanes. + * + * csi2_ddrclk_khz = external_rate / 1000 + * / (2 * num_lanes) * fmt->bpp; + */ + csi2_ddrclk_khz = div_s64(external_rate * fmt->bpp, + 2 * num_lanes * 1000); + + phy_dbg(1, phy, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); + + /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ + ths_term = 20 * csi2_ddrclk_khz / 1000000; + phy_dbg(1, phy, "ths_term: %d (0x%02x)\n", ths_term, ths_term); + + /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ + ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; + phy_dbg(1, phy, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); + + reg0 = camerarx_read(phy, CAL_CSI2_PHY_REG0); + cal_set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, + CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); + cal_set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); + cal_set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); + + phy_dbg(1, phy, "CSI2_%d_REG0 = 0x%08x\n", phy->instance, reg0); + camerarx_write(phy, CAL_CSI2_PHY_REG0, reg0); + + reg1 = camerarx_read(phy, CAL_CSI2_PHY_REG1); + cal_set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); + cal_set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); + cal_set_field(®1, TCLK_MISS, + CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); + cal_set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); + + phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x\n", phy->instance, reg1); + camerarx_write(phy, CAL_CSI2_PHY_REG1, reg1); +} + +static void cal_camerarx_power(struct cal_camerarx *phy, bool enable) +{ + u32 target_state; + unsigned int i; + + target_state = enable ? CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON : + CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF; + + cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + target_state, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK); + + for (i = 0; i < 10; i++) { + u32 current_state; + + current_state = cal_read_field(phy->cal, + CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK); + + if (current_state == target_state) + break; + + usleep_range(1000, 1100); + } + + if (i == 10) + phy_err(phy, "Failed to power %s complexio\n", + enable ? "up" : "down"); +} + +static void cal_camerarx_wait_reset(struct cal_camerarx *phy) +{ + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(750); + while (time_before(jiffies, timeout)) { + if (cal_read_field(phy->cal, + CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) + break; + usleep_range(500, 5000); + } + + if (cal_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) != + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) + phy_err(phy, "Timeout waiting for Complex IO reset done\n"); +} + +static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) +{ + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(750); + while (time_before(jiffies, timeout)) { + if (cal_read_field(phy->cal, + CAL_CSI2_TIMING(phy->instance), + CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0) + break; + usleep_range(500, 5000); + } + + if (cal_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance), + CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0) + phy_err(phy, "Timeout waiting for stop state\n"); +} + +int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) +{ + s64 external_rate; + u32 sscounter; + u32 val; + int ret; + + external_rate = cal_camerarx_get_external_rate(phy); + if (external_rate < 0) + return external_rate; + + ret = v4l2_subdev_call(phy->sensor, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { + phy_err(phy, "power on failed in subdev\n"); + return ret; + } + + /* + * CSI-2 PHY Link Initialization Sequence, according to the DRA74xP / + * DRA75xP / DRA76xP / DRA77xP TRM. The DRA71x / DRA72x and the AM65x / + * DRA80xM TRMs have a a slightly simplified sequence. + */ + + /* + * 1. Configure all CSI-2 low level protocol registers to be ready to + * receive signals/data from the CSI-2 PHY. + * + * i.-v. Configure the lanes position and polarity. + */ + cal_camerarx_lane_config(phy); + + /* + * vi.-vii. Configure D-PHY mode, enable the required lanes and + * enable the CAMERARX clock. + */ + cal_camerarx_enable(phy); + + /* + * 2. CSI PHY and link initialization sequence. + * + * a. Deassert the CSI-2 PHY reset. Do not wait for reset completion + * at this point, as it requires the external sensor to send the + * CSI-2 HS clock. + */ + cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL, + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n", + phy->instance, + cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance))); + + /* Dummy read to allow SCP reset to complete. */ + camerarx_read(phy, CAL_CSI2_PHY_REG0); + + /* Program the PHY timing parameters. */ + cal_camerarx_config(phy, external_rate, fmt); + + /* + * b. Assert the FORCERXMODE signal. + * + * The stop-state-counter is based on fclk cycles, and we always use + * the x16 and x4 settings, so stop-state-timeout = + * fclk-cycle * 16 * 4 * counter. + * + * Stop-state-timeout must be more than 100us as per CSI-2 spec, so we + * calculate a timeout that's 100us (rounding up). + */ + sscounter = DIV_ROUND_UP(clk_get_rate(phy->cal->fclk), 10000 * 16 * 4); + + val = cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance)); + cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK); + cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK); + cal_set_field(&val, sscounter, + CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK); + cal_write(phy->cal, CAL_CSI2_TIMING(phy->instance), val); + phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n", + phy->instance, + cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); + + /* Assert the FORCERXMODE signal. */ + cal_write_field(phy->cal, CAL_CSI2_TIMING(phy->instance), + 1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK); + phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n", + phy->instance, + cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); + + /* + * c. Connect pull-down on CSI-2 PHY link (using pad control). + * + * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not + * implemented. + */ + + /* + * d. Power up the CSI-2 PHY. + * e. Check whether the state status reaches the ON state. + */ + cal_camerarx_power(phy, true); + + /* + * Start the sensor to enable the CSI-2 HS clock. We can now wait for + * CSI-2 PHY reset to complete. + */ + ret = v4l2_subdev_call(phy->sensor, video, s_stream, 1); + if (ret) { + v4l2_subdev_call(phy->sensor, core, s_power, 0); + phy_err(phy, "stream on failed in subdev\n"); + return ret; + } + + cal_camerarx_wait_reset(phy); + + /* f. Wait for STOPSTATE=1 for all enabled lane modules. */ + cal_camerarx_wait_stop_state(phy); + + phy_dbg(1, phy, "CSI2_%u_REG1 = 0x%08x (bits 31-28 should be set)\n", + phy->instance, camerarx_read(phy, CAL_CSI2_PHY_REG1)); + + /* + * g. Disable pull-down on CSI-2 PHY link (using pad control). + * + * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not + * implemented. + */ + + return 0; +} + +void cal_camerarx_stop(struct cal_camerarx *phy) +{ + unsigned int i; + int ret; + + cal_camerarx_power(phy, false); + + /* Assert Complex IO Reset */ + cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); + + /* Wait for power down completion */ + for (i = 0; i < 10; i++) { + if (cal_read_field(phy->cal, + CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING) + break; + usleep_range(1000, 1100); + } + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n", + phy->instance, + cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)), i, + (i >= 10) ? "(timeout)" : ""); + + /* Disable the phy */ + cal_camerarx_disable(phy); + + if (v4l2_subdev_call(phy->sensor, video, s_stream, 0)) + phy_err(phy, "stream off failed in subdev\n"); + + ret = v4l2_subdev_call(phy->sensor, core, s_power, 0); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + phy_err(phy, "power off failed in subdev\n"); +} + +/* + * Errata i913: CSI2 LDO Needs to be disabled when module is powered on + * + * Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2 + * LDOs on the device are disabled if CSI-2 module is powered on + * (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304 + * | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high + * current draw on the module supply in active mode. + * + * Errata does not apply when CSI-2 module is powered off + * (0x4845 B304 | 0x4845 B384 [28:27] = 0x0). + * + * SW Workaround: + * Set the following register bits to disable the LDO, + * which is essentially CSI2 REG10 bit 6: + * + * Core 0: 0x4845 B828 = 0x0000 0040 + * Core 1: 0x4845 B928 = 0x0000 0040 + */ +void cal_camerarx_i913_errata(struct cal_camerarx *phy) +{ + u32 reg10 = camerarx_read(phy, CAL_CSI2_PHY_REG10); + + cal_set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); + + phy_dbg(1, phy, "CSI2_%d_REG10 = 0x%08x\n", phy->instance, reg10); + camerarx_write(phy, CAL_CSI2_PHY_REG10, reg10); +} + +/* + * Enable the expected IRQ sources + */ +void cal_camerarx_enable_irqs(struct cal_camerarx *phy) +{ + u32 val; + + const u32 cio_err_mask = + CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK | + CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK | + CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK | + CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; + + /* Enable CIO error irqs */ + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), + CAL_HL_IRQ_CIO_MASK(phy->instance)); + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), + cio_err_mask); + + /* Always enable OCPO error */ + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); + + /* Enable IRQ_WDMA_END 0/1 */ + val = 0; + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(1), val); + /* Enable IRQ_WDMA_START 0/1 */ + val = 0; + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(2), val); + /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ + cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); +} + +void cal_camerarx_disable_irqs(struct cal_camerarx *phy) +{ + u32 val; + + /* Disable CIO error irqs */ + cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), + CAL_HL_IRQ_CIO_MASK(phy->instance)); + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0); + + /* Disable IRQ_WDMA_END 0/1 */ + val = 0; + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(1), val); + /* Disable IRQ_WDMA_START 0/1 */ + val = 0; + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(2), val); + /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ + cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0); +} + +void cal_camerarx_ppi_enable(struct cal_camerarx *phy) +{ + cal_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), BIT(3)); + cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), + 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); +} + +void cal_camerarx_ppi_disable(struct cal_camerarx *phy) +{ + cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), + 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); +} + +static int cal_camerarx_regmap_init(struct cal_dev *cal, + struct cal_camerarx *phy) +{ + const struct cal_camerarx_data *phy_data; + unsigned int i; + + if (!cal->data) + return -EINVAL; + + phy_data = &cal->data->camerarx[phy->instance]; + + for (i = 0; i < F_MAX_FIELDS; i++) { + struct reg_field field = { + .reg = cal->syscon_camerrx_offset, + .lsb = phy_data->fields[i].lsb, + .msb = phy_data->fields[i].msb, + }; + + /* + * Here we update the reg offset with the + * value found in DT + */ + phy->fields[i] = devm_regmap_field_alloc(cal->dev, + cal->syscon_camerrx, + field); + if (IS_ERR(phy->fields[i])) { + cal_err(cal, "Unable to allocate regmap fields\n"); + return PTR_ERR(phy->fields[i]); + } + } + + return 0; +} + +static int cal_camerarx_parse_dt(struct cal_camerarx *phy) +{ + struct v4l2_fwnode_endpoint *endpoint = &phy->endpoint; + struct device_node *ep_node; + char data_lanes[V4L2_FWNODE_CSI2_MAX_DATA_LANES * 2]; + unsigned int i; + int ret; + + /* + * Find the endpoint node for the port corresponding to the PHY + * instance, and parse its CSI-2-related properties. + */ + ep_node = of_graph_get_endpoint_by_regs(phy->cal->dev->of_node, + phy->instance, 0); + if (!ep_node) { + /* + * The endpoint is not mandatory, not all PHY instances need to + * be connected in DT. + */ + phy_dbg(3, phy, "Port has no endpoint\n"); + return 0; + } + + endpoint->bus_type = V4L2_MBUS_CSI2_DPHY; + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint); + if (ret < 0) { + phy_err(phy, "Failed to parse endpoint\n"); + goto done; + } + + for (i = 0; i < endpoint->bus.mipi_csi2.num_data_lanes; i++) { + unsigned int lane = endpoint->bus.mipi_csi2.data_lanes[i]; + + if (lane > 4) { + phy_err(phy, "Invalid position %u for data lane %u\n", + lane, i); + ret = -EINVAL; + goto done; + } + + data_lanes[i*2] = '0' + lane; + data_lanes[i*2+1] = ' '; + } + + data_lanes[i*2-1] = '\0'; + + phy_dbg(3, phy, + "CSI-2 bus: clock lane <%u>, data lanes <%s>, flags 0x%08x\n", + endpoint->bus.mipi_csi2.clock_lane, data_lanes, + endpoint->bus.mipi_csi2.flags); + + /* Retrieve the connected device and store it for later use. */ + phy->sensor_node = of_graph_get_remote_port_parent(ep_node); + if (!phy->sensor_node) { + phy_dbg(3, phy, "Can't get remote parent\n"); + ret = -EINVAL; + goto done; + } + + phy_dbg(1, phy, "Found connected device %pOFn\n", phy->sensor_node); + +done: + of_node_put(ep_node); + return ret; +} + +struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, + unsigned int instance) +{ + struct platform_device *pdev = to_platform_device(cal->dev); + struct cal_camerarx *phy; + int ret; + + phy = kzalloc(sizeof(*phy), GFP_KERNEL); + if (!phy) + return ERR_PTR(-ENOMEM); + + phy->cal = cal; + phy->instance = instance; + + phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + (instance == 0) ? + "cal_rx_core0" : + "cal_rx_core1"); + phy->base = devm_ioremap_resource(cal->dev, phy->res); + if (IS_ERR(phy->base)) { + cal_err(cal, "failed to ioremap\n"); + ret = PTR_ERR(phy->base); + goto error; + } + + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", + phy->res->name, &phy->res->start, &phy->res->end); + + ret = cal_camerarx_regmap_init(cal, phy); + if (ret) + goto error; + + ret = cal_camerarx_parse_dt(phy); + if (ret) + goto error; + + return phy; + +error: + kfree(phy); + return ERR_PTR(ret); +} + +void cal_camerarx_destroy(struct cal_camerarx *phy) +{ + if (!phy) + return; + + of_node_put(phy->sensor_node); + kfree(phy); +} diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index c2252ed36d68..59a0266b1f39 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -10,12 +10,10 @@ */ #include -#include #include #include #include #include -#include #include #include #include @@ -25,9 +23,7 @@ #include #include #include -#include #include -#include #include #include @@ -155,682 +151,6 @@ void cal_quickdump_regs(struct cal_dev *cal) } } -/* ------------------------------------------------------------------ - * CAMERARX Management - * ------------------------------------------------------------------ - */ - -static inline u32 camerarx_read(struct cal_camerarx *phy, u32 offset) -{ - return ioread32(phy->base + offset); -} - -static inline void camerarx_write(struct cal_camerarx *phy, u32 offset, u32 val) -{ - iowrite32(val, phy->base + offset); -} - -static s64 cal_camerarx_get_external_rate(struct cal_camerarx *phy) -{ - struct v4l2_ctrl *ctrl; - s64 rate; - - ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); - if (!ctrl) { - phy_err(phy, "no pixel rate control in subdev: %s\n", - phy->sensor->name); - return -EPIPE; - } - - rate = v4l2_ctrl_g_ctrl_int64(ctrl); - phy_dbg(3, phy, "sensor Pixel Rate: %llu\n", rate); - - return rate; -} - -static void cal_camerarx_lane_config(struct cal_camerarx *phy) -{ - u32 val = cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)); - u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; - u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; - struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &phy->endpoint.bus.mipi_csi2; - int lane; - - cal_set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); - cal_set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask); - for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) { - /* - * Every lane are one nibble apart starting with the - * clock followed by the data lanes so shift masks by 4. - */ - lane_mask <<= 4; - polarity_mask <<= 4; - cal_set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask); - cal_set_field(&val, mipi_csi2->lane_polarities[lane + 1], - polarity_mask); - } - - cal_write(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), val); - phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", - phy->instance, val); -} - -static void cal_camerarx_enable(struct cal_camerarx *phy) -{ - u32 num_lanes = phy->cal->data->camerarx[phy->instance].num_lanes; - - regmap_field_write(phy->fields[F_CAMMODE], 0); - /* Always enable all lanes at the phy control level */ - regmap_field_write(phy->fields[F_LANEENABLE], (1 << num_lanes) - 1); - /* F_CSI_MODE is not present on every architecture */ - if (phy->fields[F_CSI_MODE]) - regmap_field_write(phy->fields[F_CSI_MODE], 1); - regmap_field_write(phy->fields[F_CTRLCLKEN], 1); -} - -static void cal_camerarx_disable(struct cal_camerarx *phy) -{ - regmap_field_write(phy->fields[F_CTRLCLKEN], 0); -} - -/* - * TCLK values are OK at their reset values - */ -#define TCLK_TERM 0 -#define TCLK_MISS 1 -#define TCLK_SETTLE 14 - -static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate, - const struct cal_fmt *fmt) -{ - unsigned int reg0, reg1; - unsigned int ths_term, ths_settle; - unsigned int csi2_ddrclk_khz; - struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &phy->endpoint.bus.mipi_csi2; - u32 num_lanes = mipi_csi2->num_data_lanes; - - /* DPHY timing configuration */ - - /* - * CSI-2 is DDR and we only count used lanes. - * - * csi2_ddrclk_khz = external_rate / 1000 - * / (2 * num_lanes) * fmt->bpp; - */ - csi2_ddrclk_khz = div_s64(external_rate * fmt->bpp, - 2 * num_lanes * 1000); - - phy_dbg(1, phy, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); - - /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ - ths_term = 20 * csi2_ddrclk_khz / 1000000; - phy_dbg(1, phy, "ths_term: %d (0x%02x)\n", ths_term, ths_term); - - /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ - ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; - phy_dbg(1, phy, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); - - reg0 = camerarx_read(phy, CAL_CSI2_PHY_REG0); - cal_set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, - CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); - cal_set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); - cal_set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); - - phy_dbg(1, phy, "CSI2_%d_REG0 = 0x%08x\n", phy->instance, reg0); - camerarx_write(phy, CAL_CSI2_PHY_REG0, reg0); - - reg1 = camerarx_read(phy, CAL_CSI2_PHY_REG1); - cal_set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); - cal_set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); - cal_set_field(®1, TCLK_MISS, - CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); - cal_set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); - - phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x\n", phy->instance, reg1); - camerarx_write(phy, CAL_CSI2_PHY_REG1, reg1); -} - -static void cal_camerarx_power(struct cal_camerarx *phy, bool enable) -{ - u32 target_state; - unsigned int i; - - target_state = enable ? CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON : - CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF; - - cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), - target_state, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK); - - for (i = 0; i < 10; i++) { - u32 current_state; - - current_state = cal_read_field(phy->cal, - CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK); - - if (current_state == target_state) - break; - - usleep_range(1000, 1100); - } - - if (i == 10) - phy_err(phy, "Failed to power %s complexio\n", - enable ? "up" : "down"); -} - -static void cal_camerarx_wait_reset(struct cal_camerarx *phy) -{ - unsigned long timeout; - - timeout = jiffies + msecs_to_jiffies(750); - while (time_before(jiffies, timeout)) { - if (cal_read_field(phy->cal, - CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) - break; - usleep_range(500, 5000); - } - - if (cal_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) != - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) - phy_err(phy, "Timeout waiting for Complex IO reset done\n"); -} - -static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) -{ - unsigned long timeout; - - timeout = jiffies + msecs_to_jiffies(750); - while (time_before(jiffies, timeout)) { - if (cal_read_field(phy->cal, - CAL_CSI2_TIMING(phy->instance), - CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0) - break; - usleep_range(500, 5000); - } - - if (cal_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance), - CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0) - phy_err(phy, "Timeout waiting for stop state\n"); -} - -int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) -{ - s64 external_rate; - u32 sscounter; - u32 val; - int ret; - - external_rate = cal_camerarx_get_external_rate(phy); - if (external_rate < 0) - return external_rate; - - ret = v4l2_subdev_call(phy->sensor, core, s_power, 1); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { - phy_err(phy, "power on failed in subdev\n"); - return ret; - } - - /* - * CSI-2 PHY Link Initialization Sequence, according to the DRA74xP / - * DRA75xP / DRA76xP / DRA77xP TRM. The DRA71x / DRA72x and the AM65x / - * DRA80xM TRMs have a a slightly simplified sequence. - */ - - /* - * 1. Configure all CSI-2 low level protocol registers to be ready to - * receive signals/data from the CSI-2 PHY. - * - * i.-v. Configure the lanes position and polarity. - */ - cal_camerarx_lane_config(phy); - - /* - * vi.-vii. Configure D-PHY mode, enable the required lanes and - * enable the CAMERARX clock. - */ - cal_camerarx_enable(phy); - - /* - * 2. CSI PHY and link initialization sequence. - * - * a. Deassert the CSI-2 PHY reset. Do not wait for reset completion - * at this point, as it requires the external sensor to send the - * CSI-2 HS clock. - */ - cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL, - CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); - phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n", - phy->instance, - cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance))); - - /* Dummy read to allow SCP reset to complete. */ - camerarx_read(phy, CAL_CSI2_PHY_REG0); - - /* Program the PHY timing parameters. */ - cal_camerarx_config(phy, external_rate, fmt); - - /* - * b. Assert the FORCERXMODE signal. - * - * The stop-state-counter is based on fclk cycles, and we always use - * the x16 and x4 settings, so stop-state-timeout = - * fclk-cycle * 16 * 4 * counter. - * - * Stop-state-timeout must be more than 100us as per CSI-2 spec, so we - * calculate a timeout that's 100us (rounding up). - */ - sscounter = DIV_ROUND_UP(clk_get_rate(phy->cal->fclk), 10000 * 16 * 4); - - val = cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance)); - cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK); - cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK); - cal_set_field(&val, sscounter, - CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK); - cal_write(phy->cal, CAL_CSI2_TIMING(phy->instance), val); - phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n", - phy->instance, - cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); - - /* Assert the FORCERXMODE signal. */ - cal_write_field(phy->cal, CAL_CSI2_TIMING(phy->instance), - 1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK); - phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n", - phy->instance, - cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); - - /* - * c. Connect pull-down on CSI-2 PHY link (using pad control). - * - * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not - * implemented. - */ - - /* - * d. Power up the CSI-2 PHY. - * e. Check whether the state status reaches the ON state. - */ - cal_camerarx_power(phy, true); - - /* - * Start the sensor to enable the CSI-2 HS clock. We can now wait for - * CSI-2 PHY reset to complete. - */ - ret = v4l2_subdev_call(phy->sensor, video, s_stream, 1); - if (ret) { - v4l2_subdev_call(phy->sensor, core, s_power, 0); - phy_err(phy, "stream on failed in subdev\n"); - return ret; - } - - cal_camerarx_wait_reset(phy); - - /* f. Wait for STOPSTATE=1 for all enabled lane modules. */ - cal_camerarx_wait_stop_state(phy); - - phy_dbg(1, phy, "CSI2_%u_REG1 = 0x%08x (bits 31-28 should be set)\n", - phy->instance, camerarx_read(phy, CAL_CSI2_PHY_REG1)); - - /* - * g. Disable pull-down on CSI-2 PHY link (using pad control). - * - * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not - * implemented. - */ - - return 0; -} - -void cal_camerarx_stop(struct cal_camerarx *phy) -{ - unsigned int i; - int ret; - - cal_camerarx_power(phy, false); - - /* Assert Complex IO Reset */ - cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, - CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); - - /* Wait for power down completion */ - for (i = 0; i < 10; i++) { - if (cal_read_field(phy->cal, - CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING) - break; - usleep_range(1000, 1100); - } - phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n", - phy->instance, - cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)), i, - (i >= 10) ? "(timeout)" : ""); - - /* Disable the phy */ - cal_camerarx_disable(phy); - - if (v4l2_subdev_call(phy->sensor, video, s_stream, 0)) - phy_err(phy, "stream off failed in subdev\n"); - - ret = v4l2_subdev_call(phy->sensor, core, s_power, 0); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) - phy_err(phy, "power off failed in subdev\n"); -} - -/* - * Errata i913: CSI2 LDO Needs to be disabled when module is powered on - * - * Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2 - * LDOs on the device are disabled if CSI-2 module is powered on - * (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304 - * | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high - * current draw on the module supply in active mode. - * - * Errata does not apply when CSI-2 module is powered off - * (0x4845 B304 | 0x4845 B384 [28:27] = 0x0). - * - * SW Workaround: - * Set the following register bits to disable the LDO, - * which is essentially CSI2 REG10 bit 6: - * - * Core 0: 0x4845 B828 = 0x0000 0040 - * Core 1: 0x4845 B928 = 0x0000 0040 - */ -static void cal_camerarx_i913_errata(struct cal_camerarx *phy) -{ - u32 reg10 = camerarx_read(phy, CAL_CSI2_PHY_REG10); - - cal_set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); - - phy_dbg(1, phy, "CSI2_%d_REG10 = 0x%08x\n", phy->instance, reg10); - camerarx_write(phy, CAL_CSI2_PHY_REG10, reg10); -} - -/* - * Enable the expected IRQ sources - */ -void cal_camerarx_enable_irqs(struct cal_camerarx *phy) -{ - u32 val; - - const u32 cio_err_mask = - CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK | - CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK | - CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK | - CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; - - /* Enable CIO error irqs */ - cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), - CAL_HL_IRQ_CIO_MASK(phy->instance)); - cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), - cio_err_mask); - - /* Always enable OCPO error */ - cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); - - /* Enable IRQ_WDMA_END 0/1 */ - val = 0; - cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - cal_write(phy->cal, CAL_HL_IRQENABLE_SET(1), val); - /* Enable IRQ_WDMA_START 0/1 */ - val = 0; - cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - cal_write(phy->cal, CAL_HL_IRQENABLE_SET(2), val); - /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); -} - -void cal_camerarx_disable_irqs(struct cal_camerarx *phy) -{ - u32 val; - - /* Disable CIO error irqs */ - cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), - CAL_HL_IRQ_CIO_MASK(phy->instance)); - cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), - 0); - - /* Disable IRQ_WDMA_END 0/1 */ - val = 0; - cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(1), val); - /* Disable IRQ_WDMA_START 0/1 */ - val = 0; - cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(2), val); - /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0); -} - -void cal_camerarx_ppi_enable(struct cal_camerarx *phy) -{ - cal_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), BIT(3)); - cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), - 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); -} - -void cal_camerarx_ppi_disable(struct cal_camerarx *phy) -{ - cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), - 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); -} - -static int cal_camerarx_regmap_init(struct cal_dev *cal, - struct cal_camerarx *phy) -{ - const struct cal_camerarx_data *phy_data; - unsigned int i; - - if (!cal->data) - return -EINVAL; - - phy_data = &cal->data->camerarx[phy->instance]; - - for (i = 0; i < F_MAX_FIELDS; i++) { - struct reg_field field = { - .reg = cal->syscon_camerrx_offset, - .lsb = phy_data->fields[i].lsb, - .msb = phy_data->fields[i].msb, - }; - - /* - * Here we update the reg offset with the - * value found in DT - */ - phy->fields[i] = devm_regmap_field_alloc(cal->dev, - cal->syscon_camerrx, - field); - if (IS_ERR(phy->fields[i])) { - cal_err(cal, "Unable to allocate regmap fields\n"); - return PTR_ERR(phy->fields[i]); - } - } - - return 0; -} - -static int cal_camerarx_parse_dt(struct cal_camerarx *phy) -{ - struct v4l2_fwnode_endpoint *endpoint = &phy->endpoint; - struct device_node *ep_node; - char data_lanes[V4L2_FWNODE_CSI2_MAX_DATA_LANES * 2]; - unsigned int i; - int ret; - - /* - * Find the endpoint node for the port corresponding to the PHY - * instance, and parse its CSI-2-related properties. - */ - ep_node = of_graph_get_endpoint_by_regs(phy->cal->dev->of_node, - phy->instance, 0); - if (!ep_node) { - /* - * The endpoint is not mandatory, not all PHY instances need to - * be connected in DT. - */ - phy_dbg(3, phy, "Port has no endpoint\n"); - return 0; - } - - endpoint->bus_type = V4L2_MBUS_CSI2_DPHY; - ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint); - if (ret < 0) { - phy_err(phy, "Failed to parse endpoint\n"); - goto done; - } - - for (i = 0; i < endpoint->bus.mipi_csi2.num_data_lanes; i++) { - unsigned int lane = endpoint->bus.mipi_csi2.data_lanes[i]; - - if (lane > 4) { - phy_err(phy, "Invalid position %u for data lane %u\n", - lane, i); - ret = -EINVAL; - goto done; - } - - data_lanes[i*2] = '0' + lane; - data_lanes[i*2+1] = ' '; - } - - data_lanes[i*2-1] = '\0'; - - phy_dbg(3, phy, - "CSI-2 bus: clock lane <%u>, data lanes <%s>, flags 0x%08x\n", - endpoint->bus.mipi_csi2.clock_lane, data_lanes, - endpoint->bus.mipi_csi2.flags); - - /* Retrieve the connected device and store it for later use. */ - phy->sensor_node = of_graph_get_remote_port_parent(ep_node); - if (!phy->sensor_node) { - phy_dbg(3, phy, "Can't get remote parent\n"); - ret = -EINVAL; - goto done; - } - - phy_dbg(1, phy, "Found connected device %pOFn\n", phy->sensor_node); - -done: - of_node_put(ep_node); - return ret; -} - -static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, - unsigned int instance) -{ - struct platform_device *pdev = to_platform_device(cal->dev); - struct cal_camerarx *phy; - int ret; - - phy = kzalloc(sizeof(*phy), GFP_KERNEL); - if (!phy) - return ERR_PTR(-ENOMEM); - - phy->cal = cal; - phy->instance = instance; - - phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - (instance == 0) ? - "cal_rx_core0" : - "cal_rx_core1"); - phy->base = devm_ioremap_resource(cal->dev, phy->res); - if (IS_ERR(phy->base)) { - cal_err(cal, "failed to ioremap\n"); - ret = PTR_ERR(phy->base); - goto error; - } - - cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", - phy->res->name, &phy->res->start, &phy->res->end); - - ret = cal_camerarx_regmap_init(cal, phy); - if (ret) - goto error; - - ret = cal_camerarx_parse_dt(phy); - if (ret) - goto error; - - return phy; - -error: - kfree(phy); - return ERR_PTR(ret); -} - -static void cal_camerarx_destroy(struct cal_camerarx *phy) -{ - if (!phy) - return; - - of_node_put(phy->sensor_node); - kfree(phy); -} - -static int cal_camerarx_init_regmap(struct cal_dev *cal) -{ - struct platform_device *pdev = to_platform_device(cal->dev); - struct device_node *np = cal->dev->of_node; - struct regmap_config config = { }; - struct regmap *syscon; - struct resource *res; - unsigned int offset; - void __iomem *base; - - syscon = syscon_regmap_lookup_by_phandle_args(np, "ti,camerrx-control", - 1, &offset); - if (!IS_ERR(syscon)) { - cal->syscon_camerrx = syscon; - cal->syscon_camerrx_offset = offset; - return 0; - } - - dev_warn(cal->dev, "failed to get ti,camerrx-control: %ld\n", - PTR_ERR(syscon)); - - /* - * Backward DTS compatibility. If syscon entry is not present then - * check if the camerrx_control resource is present. - */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "camerrx_control"); - base = devm_ioremap_resource(cal->dev, res); - if (IS_ERR(base)) { - cal_err(cal, "failed to ioremap camerrx_control\n"); - return PTR_ERR(base); - } - - cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", - res->name, &res->start, &res->end); - - config.reg_bits = 32; - config.reg_stride = 4; - config.val_bits = 32; - config.max_register = resource_size(res) - 4; - - syscon = regmap_init_mmio(NULL, base, &config); - if (IS_ERR(syscon)) { - pr_err("regmap init failed\n"); - return PTR_ERR(syscon); - } - - /* - * In this case the base already point to the direct CM register so no - * need for an offset. - */ - cal->syscon_camerrx = syscon; - cal->syscon_camerrx_offset = 0; - - return 0; -} - /* ------------------------------------------------------------------ * Context Management * ------------------------------------------------------------------ @@ -1362,6 +682,63 @@ static void cal_get_hwinfo(struct cal_dev *cal) hwinfo, CAL_HL_HWINFO_VALUE); } +static int cal_init_camerarx_regmap(struct cal_dev *cal) +{ + struct platform_device *pdev = to_platform_device(cal->dev); + struct device_node *np = cal->dev->of_node; + struct regmap_config config = { }; + struct regmap *syscon; + struct resource *res; + unsigned int offset; + void __iomem *base; + + syscon = syscon_regmap_lookup_by_phandle_args(np, "ti,camerrx-control", + 1, &offset); + if (!IS_ERR(syscon)) { + cal->syscon_camerrx = syscon; + cal->syscon_camerrx_offset = offset; + return 0; + } + + dev_warn(cal->dev, "failed to get ti,camerrx-control: %ld\n", + PTR_ERR(syscon)); + + /* + * Backward DTS compatibility. If syscon entry is not present then + * check if the camerrx_control resource is present. + */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "camerrx_control"); + base = devm_ioremap_resource(cal->dev, res); + if (IS_ERR(base)) { + cal_err(cal, "failed to ioremap camerrx_control\n"); + return PTR_ERR(base); + } + + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", + res->name, &res->start, &res->end); + + config.reg_bits = 32; + config.reg_stride = 4; + config.val_bits = 32; + config.max_register = resource_size(res) - 4; + + syscon = regmap_init_mmio(NULL, base, &config); + if (IS_ERR(syscon)) { + pr_err("regmap init failed\n"); + return PTR_ERR(syscon); + } + + /* + * In this case the base already point to the direct CM register so no + * need for an offset. + */ + cal->syscon_camerrx = syscon; + cal->syscon_camerrx_offset = 0; + + return 0; +} + static int cal_probe(struct platform_device *pdev) { struct cal_dev *cal; @@ -1391,7 +768,7 @@ static int cal_probe(struct platform_device *pdev) return PTR_ERR(cal->fclk); } - ret = cal_camerarx_init_regmap(cal); + ret = cal_init_camerarx_regmap(cal); if (ret < 0) return ret; diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h index 44f2836b6543..e496083715d2 100644 --- a/drivers/media/platform/ti-vpe/cal.h +++ b/drivers/media/platform/ti-vpe/cal.h @@ -241,12 +241,17 @@ static inline void cal_set_field(u32 *valp, u32 field, u32 mask) void cal_quickdump_regs(struct cal_dev *cal); +void cal_camerarx_disable(struct cal_camerarx *phy); int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt); void cal_camerarx_stop(struct cal_camerarx *phy); void cal_camerarx_enable_irqs(struct cal_camerarx *phy); void cal_camerarx_disable_irqs(struct cal_camerarx *phy); void cal_camerarx_ppi_enable(struct cal_camerarx *phy); void cal_camerarx_ppi_disable(struct cal_camerarx *phy); +void cal_camerarx_i913_errata(struct cal_camerarx *phy); +struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, + unsigned int instance); +void cal_camerarx_destroy(struct cal_camerarx *phy); void cal_ctx_csi2_config(struct cal_ctx *ctx); void cal_ctx_pix_proc_config(struct cal_ctx *ctx); -- cgit v1.2.3 From 9c487b0b0ea7ff22127fe99a7f67657d8730ff94 Mon Sep 17 00:00:00 2001 From: Evgeny Novikov Date: Fri, 10 Jul 2020 11:02:23 +0200 Subject: media: vpss: clean up resources in init If platform_driver_register() fails within vpss_init() resources are not cleaned up. The patch fixes this issue by introducing the corresponding error handling. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Evgeny Novikov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpss.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/davinci/vpss.c index d38d2bbb6f0f..7000f0bf0b35 100644 --- a/drivers/media/platform/davinci/vpss.c +++ b/drivers/media/platform/davinci/vpss.c @@ -505,19 +505,31 @@ static void vpss_exit(void) static int __init vpss_init(void) { + int ret; + if (!request_mem_region(VPSS_CLK_CTRL, 4, "vpss_clock_control")) return -EBUSY; oper_cfg.vpss_regs_base2 = ioremap(VPSS_CLK_CTRL, 4); if (unlikely(!oper_cfg.vpss_regs_base2)) { - release_mem_region(VPSS_CLK_CTRL, 4); - return -ENOMEM; + ret = -ENOMEM; + goto err_ioremap; } writel(VPSS_CLK_CTRL_VENCCLKEN | - VPSS_CLK_CTRL_DACCLKEN, oper_cfg.vpss_regs_base2); + VPSS_CLK_CTRL_DACCLKEN, oper_cfg.vpss_regs_base2); + + ret = platform_driver_register(&vpss_driver); + if (ret) + goto err_pd_register; + + return 0; - return platform_driver_register(&vpss_driver); +err_pd_register: + iounmap(oper_cfg.vpss_regs_base2); +err_ioremap: + release_mem_region(VPSS_CLK_CTRL, 4); + return ret; } subsys_initcall(vpss_init); module_exit(vpss_exit); -- cgit v1.2.3 From 9dc75e79c0dec0eb9363793efbd5f16d134169dc Mon Sep 17 00:00:00 2001 From: Kaaira Gupta Date: Sun, 5 Jul 2020 15:53:04 +0200 Subject: media: tpg: change char argument to const char Change the argument of type char * to const char * for function tpg_gen_text(). This function should take in a const char * as opposed to char * as it does not make changes to the text. This issue was found while passing the order of colors of tpg generated test image (which is a const char *) to this function. Signed-off-by: Kaaira Gupta Reviewed-by: Helen Koike Reviewed-by: Kieran Bingham Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/v4l2-tpg/v4l2-tpg-core.c | 10 +++++----- include/media/tpg/v4l2-tpg.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c index 50f1e0b28b25..dde22a4cbd6c 100644 --- a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c +++ b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c @@ -1927,34 +1927,34 @@ typedef struct { u16 __; u8 _; } __packed x24; static noinline void tpg_print_str_2(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], unsigned p, unsigned first, unsigned div, unsigned step, - int y, int x, char *text, unsigned len) + int y, int x, const char *text, unsigned len) { PRINTSTR(u8); } static noinline void tpg_print_str_4(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], unsigned p, unsigned first, unsigned div, unsigned step, - int y, int x, char *text, unsigned len) + int y, int x, const char *text, unsigned len) { PRINTSTR(u16); } static noinline void tpg_print_str_6(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], unsigned p, unsigned first, unsigned div, unsigned step, - int y, int x, char *text, unsigned len) + int y, int x, const char *text, unsigned len) { PRINTSTR(x24); } static noinline void tpg_print_str_8(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], unsigned p, unsigned first, unsigned div, unsigned step, - int y, int x, char *text, unsigned len) + int y, int x, const char *text, unsigned len) { PRINTSTR(u32); } void tpg_gen_text(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], - int y, int x, char *text) + int y, int x, const char *text) { unsigned step = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1; unsigned div = step; diff --git a/include/media/tpg/v4l2-tpg.h b/include/media/tpg/v4l2-tpg.h index eb191e85d363..9749ed409856 100644 --- a/include/media/tpg/v4l2-tpg.h +++ b/include/media/tpg/v4l2-tpg.h @@ -241,7 +241,7 @@ void tpg_log_status(struct tpg_data *tpg); void tpg_set_font(const u8 *f); void tpg_gen_text(const struct tpg_data *tpg, - u8 *basep[TPG_MAX_PLANES][2], int y, int x, char *text); + u8 *basep[TPG_MAX_PLANES][2], int y, int x, const char *text); void tpg_calc_text_basep(struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], unsigned p, u8 *vbuf); unsigned tpg_g_interleaved_plane(const struct tpg_data *tpg, unsigned buf_line); -- cgit v1.2.3 From 7a785081a16968a28adce223c572111d2d57a83e Mon Sep 17 00:00:00 2001 From: Kaaira Gupta Date: Sun, 5 Jul 2020 15:53:05 +0200 Subject: media: tpg: Add function to return colors' order of test image Currently there is no method to know the correct order of the colors for a test image generated by tpg. Write a function that returns a string of colors' order given a tpg. It returns a NULL pointer in case of test patterns which do not have a well defined colors' order. Hence add a NULL check for text in tpg_gen_text(). [hverkuil: white -> White (for consistency)] Signed-off-by: Kaaira Gupta Reviewed-by: Kieran Bingham Reviewed-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/v4l2-tpg/v4l2-tpg-core.c | 30 +++++++++++++++++++++++++-- include/media/tpg/v4l2-tpg.h | 1 + 2 files changed, 29 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c index dde22a4cbd6c..630a75e0eeb1 100644 --- a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c +++ b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c @@ -1959,12 +1959,14 @@ void tpg_gen_text(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], unsigned step = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1; unsigned div = step; unsigned first = 0; - unsigned len = strlen(text); + unsigned len; unsigned p; - if (font8x16 == NULL || basep == NULL) + if (font8x16 == NULL || basep == NULL || text == NULL) return; + len = strlen(text); + /* Checks if it is possible to show string */ if (y + 16 >= tpg->compose.height || x + 8 >= tpg->compose.width) return; @@ -2006,6 +2008,30 @@ void tpg_gen_text(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], } EXPORT_SYMBOL_GPL(tpg_gen_text); +const char *tpg_g_color_order(const struct tpg_data *tpg) +{ + switch (tpg->pattern) { + case TPG_PAT_75_COLORBAR: + case TPG_PAT_100_COLORBAR: + case TPG_PAT_CSC_COLORBAR: + case TPG_PAT_100_HCOLORBAR: + return "White, yellow, cyan, green, magenta, red, blue, black"; + case TPG_PAT_BLACK: + return "Black"; + case TPG_PAT_WHITE: + return "White"; + case TPG_PAT_RED: + return "Red"; + case TPG_PAT_GREEN: + return "Green"; + case TPG_PAT_BLUE: + return "Blue"; + default: + return NULL; + } +} +EXPORT_SYMBOL_GPL(tpg_g_color_order); + void tpg_update_mv_step(struct tpg_data *tpg) { int factor = tpg->mv_hor_mode > TPG_MOVE_NONE ? -1 : 1; diff --git a/include/media/tpg/v4l2-tpg.h b/include/media/tpg/v4l2-tpg.h index 9749ed409856..0b0ddb87380e 100644 --- a/include/media/tpg/v4l2-tpg.h +++ b/include/media/tpg/v4l2-tpg.h @@ -252,6 +252,7 @@ void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc); void tpg_s_crop_compose(struct tpg_data *tpg, const struct v4l2_rect *crop, const struct v4l2_rect *compose); +const char *tpg_g_color_order(const struct tpg_data *tpg); static inline void tpg_s_pattern(struct tpg_data *tpg, enum tpg_pattern pattern) { -- cgit v1.2.3 From 5f3fb5c54d67670fa6743d2434a5bd43a97c01de Mon Sep 17 00:00:00 2001 From: Kaaira Gupta Date: Sun, 5 Jul 2020 15:53:06 +0200 Subject: media: vimc: Add a control to display info on test image Add a control in VIMC to display information such as the correct order of colors for a given test pattern, counter, brightness, hue, saturation, contrast, width and height at sensor over test image. Signed-off-by: Kaaira Gupta Acked-by: Helen Koike Reviewed-by: Kieran Bingham Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/test-drivers/vimc/Kconfig | 2 + drivers/media/test-drivers/vimc/vimc-common.h | 1 + drivers/media/test-drivers/vimc/vimc-core.c | 10 ++++ drivers/media/test-drivers/vimc/vimc-sensor.c | 71 +++++++++++++++++++++++++++ 4 files changed, 84 insertions(+) (limited to 'drivers/media') diff --git a/drivers/media/test-drivers/vimc/Kconfig b/drivers/media/test-drivers/vimc/Kconfig index 4068a67585f9..da4b2ad6e40c 100644 --- a/drivers/media/test-drivers/vimc/Kconfig +++ b/drivers/media/test-drivers/vimc/Kconfig @@ -2,6 +2,8 @@ config VIDEO_VIMC tristate "Virtual Media Controller Driver (VIMC)" depends on VIDEO_DEV && VIDEO_V4L2 + select FONT_SUPPORT + select FONT_8x16 select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select VIDEOBUF2_VMALLOC diff --git a/drivers/media/test-drivers/vimc/vimc-common.h b/drivers/media/test-drivers/vimc/vimc-common.h index ae163dec2459..a289434e75ba 100644 --- a/drivers/media/test-drivers/vimc/vimc-common.h +++ b/drivers/media/test-drivers/vimc/vimc-common.h @@ -20,6 +20,7 @@ #define VIMC_CID_VIMC_CLASS (0x00f00000 | 1) #define VIMC_CID_TEST_PATTERN (VIMC_CID_VIMC_BASE + 0) #define VIMC_CID_MEAN_WIN_SIZE (VIMC_CID_VIMC_BASE + 1) +#define VIMC_CID_OSD_TEXT_MODE (VIMC_CID_VIMC_BASE + 2) #define VIMC_FRAME_MAX_WIDTH 4096 #define VIMC_FRAME_MAX_HEIGHT 2160 diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c index 11210aaa2551..4b0ae6f51d76 100644 --- a/drivers/media/test-drivers/vimc/vimc-core.c +++ b/drivers/media/test-drivers/vimc/vimc-core.c @@ -5,10 +5,12 @@ * Copyright (C) 2015-2017 Helen Koike */ +#include #include #include #include #include +#include #include #include "vimc-common.h" @@ -263,11 +265,19 @@ err_v4l2_unregister: static int vimc_probe(struct platform_device *pdev) { + const struct font_desc *font = find_font("VGA8x16"); struct vimc_device *vimc; int ret; dev_dbg(&pdev->dev, "probe"); + if (!font) { + dev_err(&pdev->dev, "could not find font\n"); + return -ENODEV; + } + + tpg_set_font(font->data); + vimc = kzalloc(sizeof(*vimc), GFP_KERNEL); if (!vimc) return -ENOMEM; diff --git a/drivers/media/test-drivers/vimc/vimc-sensor.c b/drivers/media/test-drivers/vimc/vimc-sensor.c index a2f09ac9a360..ba5db5a150b4 100644 --- a/drivers/media/test-drivers/vimc/vimc-sensor.c +++ b/drivers/media/test-drivers/vimc/vimc-sensor.c @@ -14,11 +14,19 @@ #include "vimc-common.h" +enum vimc_sen_osd_mode { + VIMC_SEN_OSD_SHOW_ALL = 0, + VIMC_SEN_OSD_SHOW_COUNTERS = 1, + VIMC_SEN_OSD_SHOW_NONE = 2 +}; + struct vimc_sen_device { struct vimc_ent_device ved; struct v4l2_subdev sd; struct tpg_data tpg; u8 *frame; + enum vimc_sen_osd_mode osd_value; + u64 start_stream_ts; /* The active format */ struct v4l2_mbus_framefmt mbus_format; struct v4l2_ctrl_handler hdl; @@ -187,8 +195,49 @@ static void *vimc_sen_process_frame(struct vimc_ent_device *ved, { struct vimc_sen_device *vsen = container_of(ved, struct vimc_sen_device, ved); + const unsigned int line_height = 16; + u8 *basep[TPG_MAX_PLANES][2]; + unsigned int line = 1; + char str[100]; tpg_fill_plane_buffer(&vsen->tpg, 0, 0, vsen->frame); + tpg_calc_text_basep(&vsen->tpg, basep, 0, vsen->frame); + switch (vsen->osd_value) { + case VIMC_SEN_OSD_SHOW_ALL: { + const char *order = tpg_g_color_order(&vsen->tpg); + + tpg_gen_text(&vsen->tpg, basep, line++ * line_height, + 16, order); + snprintf(str, sizeof(str), + "brightness %3d, contrast %3d, saturation %3d, hue %d ", + vsen->tpg.brightness, + vsen->tpg.contrast, + vsen->tpg.saturation, + vsen->tpg.hue); + tpg_gen_text(&vsen->tpg, basep, line++ * line_height, 16, str); + snprintf(str, sizeof(str), "sensor size: %dx%d", + vsen->mbus_format.width, + vsen->mbus_format.height); + tpg_gen_text(&vsen->tpg, basep, line++ * line_height, 16, str); + fallthrough; + } + case VIMC_SEN_OSD_SHOW_COUNTERS: { + unsigned int ms; + + ms = div_u64(ktime_get_ns() - vsen->start_stream_ts, 1000000); + snprintf(str, sizeof(str), "%02d:%02d:%02d:%03d", + (ms / (60 * 60 * 1000)) % 24, + (ms / (60 * 1000)) % 60, + (ms / 1000) % 60, + ms % 1000); + tpg_gen_text(&vsen->tpg, basep, line++ * line_height, 16, str); + break; + } + case VIMC_SEN_OSD_SHOW_NONE: + default: + break; + } + return vsen->frame; } @@ -201,6 +250,8 @@ static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable) const struct vimc_pix_map *vpix; unsigned int frame_size; + vsen->start_stream_ts = ktime_get_ns(); + /* Calculate the frame size */ vpix = vimc_pix_map_by_code(vsen->mbus_format.code); frame_size = vsen->mbus_format.width * vpix->bpp * @@ -269,6 +320,9 @@ static int vimc_sen_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_SATURATION: tpg_s_saturation(&vsen->tpg, ctrl->val); break; + case VIMC_CID_OSD_TEXT_MODE: + vsen->osd_value = ctrl->val; + break; default: return -EINVAL; } @@ -307,6 +361,22 @@ static const struct v4l2_ctrl_config vimc_sen_ctrl_test_pattern = { .qmenu = tpg_pattern_strings, }; +static const char * const vimc_ctrl_osd_mode_strings[] = { + "All", + "Counters Only", + "None", + NULL, +}; + +static const struct v4l2_ctrl_config vimc_sen_ctrl_osd_mode = { + .ops = &vimc_sen_ctrl_ops, + .id = VIMC_CID_OSD_TEXT_MODE, + .name = "Show Information", + .type = V4L2_CTRL_TYPE_MENU, + .max = ARRAY_SIZE(vimc_ctrl_osd_mode_strings) - 2, + .qmenu = vimc_ctrl_osd_mode_strings, +}; + static struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, const char *vcfg_name) { @@ -323,6 +393,7 @@ static struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, v4l2_ctrl_new_custom(&vsen->hdl, &vimc_sen_ctrl_class, NULL); v4l2_ctrl_new_custom(&vsen->hdl, &vimc_sen_ctrl_test_pattern, NULL); + v4l2_ctrl_new_custom(&vsen->hdl, &vimc_sen_ctrl_osd_mode, NULL); v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops, -- cgit v1.2.3 From c07fe5e020ff51bf4af834932035a5fb9b14efe4 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Jul 2020 17:21:45 +0200 Subject: media: cx18: Replace http links with https ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx18/cx18-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/cx18/cx18-cards.c b/drivers/media/pci/cx18/cx18-cards.c index ecbe76f1ca63..f5a30959a367 100644 --- a/drivers/media/pci/cx18/cx18-cards.c +++ b/drivers/media/pci/cx18/cx18-cards.c @@ -35,7 +35,7 @@ static struct cx18_card_tuner_i2c cx18_i2c_nxp = { .tv = { 0x61, 0x60, I2C_CLIENT_END }, }; -/* Please add new PCI IDs to: http://pci-ids.ucw.cz/ +/* Please add new PCI IDs to: https://pci-ids.ucw.cz/ This keeps the PCI ID database up to date. Note that the entries must be added under vendor 0x4444 (Conexant) as subsystem IDs. New vendor IDs should still be added to the vendor ID list. */ -- cgit v1.2.3 From 276e2ee0765941e7fa20973a4c2215df830e8483 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Jul 2020 17:31:53 +0200 Subject: media: imon: Replace http links with https ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/imon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index ed95244da894..a7962ca2ac8e 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -795,7 +795,7 @@ static ssize_t show_associate_remote(struct device *d, else strscpy(buf, "closed\n", PAGE_SIZE); - dev_info(d, "Visit http://www.lirc.org/html/imon-24g.html for instructions on how to associate your iMON 2.4G DT/LT remote\n"); + dev_info(d, "Visit https://www.lirc.org/html/imon-24g.html for instructions on how to associate your iMON 2.4G DT/LT remote\n"); mutex_unlock(&ictx->lock); return strlen(buf); } -- cgit v1.2.3 From 1cc94c17670006c4faced28d6111e605d39eb964 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Jul 2020 17:42:01 +0200 Subject: media: ivtv: Replace http links with https ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ivtv/ivtv-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/ivtv/ivtv-cards.c b/drivers/media/pci/ivtv/ivtv-cards.c index ca6daba3a34a..c8f4ed7ff2c6 100644 --- a/drivers/media/pci/ivtv/ivtv-cards.c +++ b/drivers/media/pci/ivtv/ivtv-cards.c @@ -53,7 +53,7 @@ static struct ivtv_card_tuner_i2c ivtv_i2c_tda8290 = { /********************** card configuration *******************************/ -/* Please add new PCI IDs to: http://pci-ids.ucw.cz/ +/* Please add new PCI IDs to: https://pci-ids.ucw.cz/ This keeps the PCI ID database up to date. Note that the entries must be added under vendor 0x4444 (Conexant) as subsystem IDs. New vendor IDs should still be added to the vendor ID list. */ -- cgit v1.2.3 From e6e059a8884fd4919467d72b2e5d726bd293bbe2 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Jul 2020 17:52:10 +0200 Subject: media: rtl2832_sdr: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/rtl2832_sdr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index 60d1e59d2292..720756728f2d 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -5,7 +5,7 @@ * Copyright (C) 2013 Antti Palosaari * * GNU Radio plugin "gr-kernel" for device usage will be on: - * http://git.linuxtv.org/anttip/gr-kernel.git + * https://git.linuxtv.org/anttip/gr-kernel.git */ #include "rtl2832_sdr.h" -- cgit v1.2.3 From f20c7d9141d34e4a1505ca730743edba629ef7c4 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Jul 2020 18:02:19 +0200 Subject: media: tw5864-core: Replace http links with https ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/tw5864/tw5864-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/tw5864/tw5864-core.c b/drivers/media/pci/tw5864/tw5864-core.c index 66f2a5cabc6e..282f7dfb7aaf 100644 --- a/drivers/media/pci/tw5864/tw5864-core.c +++ b/drivers/media/pci/tw5864/tw5864-core.c @@ -65,7 +65,7 @@ module_param_array(video_nr, int, NULL, 0444); MODULE_PARM_DESC(video_nr, "video devices numbers array"); /* - * Please add any new PCI IDs to: http://pci-ids.ucw.cz. This keeps + * Please add any new PCI IDs to: https://pci-ids.ucw.cz. This keeps * the PCI ID database up to date. Note that the entries must be * added under vendor 0x1797 (Techwell Inc.) as subsystem IDs. */ -- cgit v1.2.3 From 74cc09fd8d04c56b652cfb332adb61f10bc2c199 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Jul 2020 18:12:38 +0200 Subject: media: tw68: Replace http links with https ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/tw68/tw68-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/tw68/tw68-core.c b/drivers/media/pci/tw68/tw68-core.c index b45f3ffa3de5..30c1ea87f0e8 100644 --- a/drivers/media/pci/tw68/tw68-core.c +++ b/drivers/media/pci/tw68/tw68-core.c @@ -57,7 +57,7 @@ static atomic_t tw68_instance = ATOMIC_INIT(0); /* ------------------------------------------------------------------ */ /* - * Please add any new PCI IDs to: http://pci-ids.ucw.cz. This keeps + * Please add any new PCI IDs to: https://pci-ids.ucw.cz. This keeps * the PCI ID database up to date. Note that the entries must be * added under vendor 0x1797 (Techwell Inc.) as subsystem IDs. */ -- cgit v1.2.3 From 8e5652ae8d4addb99db9ccc0112827dfe3026276 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Tue, 23 Jun 2020 18:52:26 +0200 Subject: media: i2c: imx290: replace msleep(10) with usleep_range(10000, 11000) This fixes checkpatch warnings of "msleep < 20ms can sleep for up to 20ms". Signed-off-by: Andrey Konovalov Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx290.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 9c97830164e9..adcddf3204f7 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -406,7 +406,7 @@ static int imx290_set_register_array(struct imx290 *imx290, } /* Provide 10ms settle time */ - msleep(10); + usleep_range(10000, 11000); return 0; } @@ -484,14 +484,14 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) if (ctrl->val) { imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, 0x00); imx290_write_reg(imx290, IMX290_BLKLEVEL_HIGH, 0x00); - msleep(10); + usleep_range(10000, 11000); imx290_write_reg(imx290, IMX290_PGCTRL, (u8)(IMX290_PGCTRL_REGEN | IMX290_PGCTRL_THRU | IMX290_PGCTRL_MODE(ctrl->val))); } else { imx290_write_reg(imx290, IMX290_PGCTRL, 0x00); - msleep(10); + usleep_range(10000, 11000); if (imx290->bpp == 10) imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, 0x3c); -- cgit v1.2.3 From 859891228e56b991f65acc349bbe118b44752beb Mon Sep 17 00:00:00 2001 From: Dongchun Zhu Date: Tue, 30 Jun 2020 08:22:11 +0200 Subject: media: i2c: dw9768: Add DW9768 VCM driver Add a V4L2 sub-device driver for DW9768 voice coil motor, providing control to set the desired focus via IIC serial interface. Signed-off-by: Dongchun Zhu Reviewed-by: Tomasz Figa Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 1 + drivers/media/i2c/Kconfig | 12 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/dw9768.c | 554 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 568 insertions(+) create mode 100644 drivers/media/i2c/dw9768.c (limited to 'drivers/media') diff --git a/MAINTAINERS b/MAINTAINERS index 562069ba2c62..dc28f18c62bd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5229,6 +5229,7 @@ L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/dongwoon,dw9768.yaml +F: drivers/media/i2c/dw9768.c DONGWOON DW9807 LENS VOICE COIL DRIVER M: Sakari Ailus diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 6804ad57ee71..d2d86ced319d 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1254,6 +1254,18 @@ config VIDEO_DW9714 capability. This is designed for linear control of voice coil motors, controlled via I2C serial interface. +config VIDEO_DW9768 + tristate "DW9768 lens voice coil support" + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a driver for the DW9768 camera lens voice coil. + DW9768 is a 10 bit DAC with 100mA output current sink + capability. This is designed for linear control of + voice coil motors, controlled via I2C serial interface. + config VIDEO_DW9807_VCM tristate "DW9807 lens voice coil support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 993acab81b2c..33ad435bffea 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o obj-$(CONFIG_VIDEO_AD5820) += ad5820.o obj-$(CONFIG_VIDEO_AK7375) += ak7375.o obj-$(CONFIG_VIDEO_DW9714) += dw9714.o +obj-$(CONFIG_VIDEO_DW9768) += dw9768.o obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o diff --git a/drivers/media/i2c/dw9768.c b/drivers/media/i2c/dw9768.c new file mode 100644 index 000000000000..45cdd924b565 --- /dev/null +++ b/drivers/media/i2c/dw9768.c @@ -0,0 +1,554 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2020 MediaTek Inc. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DW9768_NAME "dw9768" +#define DW9768_MAX_FOCUS_POS (1024 - 1) +/* + * This sets the minimum granularity for the focus positions. + * A value of 1 gives maximum accuracy for a desired focus position + */ +#define DW9768_FOCUS_STEPS 1 + +/* + * Ring control and Power control register + * Bit[1] RING_EN + * 0: Direct mode + * 1: AAC mode (ringing control mode) + * Bit[0] PD + * 0: Normal operation mode + * 1: Power down mode + * DW9768 requires waiting time of Topr after PD reset takes place. + */ +#define DW9768_RING_PD_CONTROL_REG 0x02 +#define DW9768_PD_MODE_OFF 0x00 +#define DW9768_PD_MODE_EN BIT(0) +#define DW9768_AAC_MODE_EN BIT(1) + +/* + * DW9768 separates two registers to control the VCM position. + * One for MSB value, another is LSB value. + * DAC_MSB: D[9:8] (ADD: 0x03) + * DAC_LSB: D[7:0] (ADD: 0x04) + * D[9:0] DAC data input: positive output current = D[9:0] / 1023 * 100[mA] + */ +#define DW9768_MSB_ADDR 0x03 +#define DW9768_LSB_ADDR 0x04 +#define DW9768_STATUS_ADDR 0x05 + +/* + * AAC mode control & prescale register + * Bit[7:5] Namely AC[2:0], decide the VCM mode and operation time. + * 001 AAC2 0.48 x Tvib + * 010 AAC3 0.70 x Tvib + * 011 AAC4 0.75 x Tvib + * 101 AAC8 1.13 x Tvib + * Bit[2:0] Namely PRESC[2:0], set the internal clock dividing rate as follow. + * 000 2 + * 001 1 + * 010 1/2 + * 011 1/4 + * 100 8 + * 101 4 + */ +#define DW9768_AAC_PRESC_REG 0x06 +#define DW9768_AAC_MODE_SEL_MASK GENMASK(7, 5) +#define DW9768_CLOCK_PRE_SCALE_SEL_MASK GENMASK(2, 0) + +/* + * VCM period of vibration register + * Bit[5:0] Defined as VCM rising periodic time (Tvib) together with PRESC[2:0] + * Tvib = (6.3ms + AACT[5:0] * 0.1ms) * Dividing Rate + * Dividing Rate is the internal clock dividing rate that is defined at + * PRESCALE register (ADD: 0x06) + */ +#define DW9768_AAC_TIME_REG 0x07 + +/* + * DW9768 requires waiting time (delay time) of t_OPR after power-up, + * or in the case of PD reset taking place. + */ +#define DW9768_T_OPR_US 1000 +#define DW9768_TVIB_MS_BASE10 (64 - 1) +#define DW9768_AAC_MODE_DEFAULT 2 +#define DW9768_AAC_TIME_DEFAULT 0x20 +#define DW9768_CLOCK_PRE_SCALE_DEFAULT 1 + +/* + * This acts as the minimum granularity of lens movement. + * Keep this value power of 2, so the control steps can be + * uniformly adjusted for gradual lens movement, with desired + * number of control steps. + */ +#define DW9768_MOVE_STEPS 16 + +static const char * const dw9768_supply_names[] = { + "vin", /* Digital I/O power */ + "vdd", /* Digital core power */ +}; + +/* dw9768 device structure */ +struct dw9768 { + struct regulator_bulk_data supplies[ARRAY_SIZE(dw9768_supply_names)]; + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *focus; + struct v4l2_subdev sd; + + u32 aac_mode; + u32 aac_timing; + u32 clock_presc; + u32 move_delay_us; +}; + +static inline struct dw9768 *sd_to_dw9768(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct dw9768, sd); +} + +struct regval_list { + u8 reg_num; + u8 value; +}; + +struct dw9768_aac_mode_ot_multi { + u32 aac_mode_enum; + u32 ot_multi_base100; +}; + +struct dw9768_clk_presc_dividing_rate { + u32 clk_presc_enum; + u32 dividing_rate_base100; +}; + +static const struct dw9768_aac_mode_ot_multi aac_mode_ot_multi[] = { + {1, 48}, + {2, 70}, + {3, 75}, + {5, 113}, +}; + +static const struct dw9768_clk_presc_dividing_rate presc_dividing_rate[] = { + {0, 200}, + {1, 100}, + {2, 50}, + {3, 25}, + {4, 800}, + {5, 400}, +}; + +static u32 dw9768_find_ot_multi(u32 aac_mode_param) +{ + u32 cur_ot_multi_base100 = 70; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(aac_mode_ot_multi); i++) { + if (aac_mode_ot_multi[i].aac_mode_enum == aac_mode_param) { + cur_ot_multi_base100 = + aac_mode_ot_multi[i].ot_multi_base100; + } + } + + return cur_ot_multi_base100; +} + +static u32 dw9768_find_dividing_rate(u32 presc_param) +{ + u32 cur_clk_dividing_rate_base100 = 100; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(presc_dividing_rate); i++) { + if (presc_dividing_rate[i].clk_presc_enum == presc_param) { + cur_clk_dividing_rate_base100 = + presc_dividing_rate[i].dividing_rate_base100; + } + } + + return cur_clk_dividing_rate_base100; +} + +/* + * DW9768_AAC_PRESC_REG & DW9768_AAC_TIME_REG determine VCM operation time. + * For current VCM mode: AAC3, Operation Time would be 0.70 x Tvib. + * Tvib = (6.3ms + AACT[5:0] * 0.1MS) * Dividing Rate. + * Below is calculation of the operation delay for each step. + */ +static inline u32 dw9768_cal_move_delay(u32 aac_mode_param, u32 presc_param, + u32 aac_timing_param) +{ + u32 Tvib_us; + u32 ot_multi_base100; + u32 clk_dividing_rate_base100; + + ot_multi_base100 = dw9768_find_ot_multi(aac_mode_param); + + clk_dividing_rate_base100 = dw9768_find_dividing_rate(presc_param); + + Tvib_us = (DW9768_TVIB_MS_BASE10 + aac_timing_param) * + clk_dividing_rate_base100; + + return Tvib_us * ot_multi_base100 / 100; +} + +static int dw9768_mod_reg(struct dw9768 *dw9768, u8 reg, u8 mask, u8 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&dw9768->sd); + int ret; + + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) + return ret; + + val = ((unsigned char)ret & ~mask) | (val & mask); + + return i2c_smbus_write_byte_data(client, reg, val); +} + +static int dw9768_set_dac(struct dw9768 *dw9768, u16 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&dw9768->sd); + + /* Write VCM position to registers */ + return i2c_smbus_write_word_swapped(client, DW9768_MSB_ADDR, val); +} + +static int dw9768_init(struct dw9768 *dw9768) +{ + struct i2c_client *client = v4l2_get_subdevdata(&dw9768->sd); + int ret, val; + + /* Reset DW9768_RING_PD_CONTROL_REG to default status 0x00 */ + ret = i2c_smbus_write_byte_data(client, DW9768_RING_PD_CONTROL_REG, + DW9768_PD_MODE_OFF); + if (ret < 0) + return ret; + + /* + * DW9769 requires waiting delay time of t_OPR + * after PD reset takes place. + */ + usleep_range(DW9768_T_OPR_US, DW9768_T_OPR_US + 100); + + /* Set DW9768_RING_PD_CONTROL_REG to DW9768_AAC_MODE_EN(0x01) */ + ret = i2c_smbus_write_byte_data(client, DW9768_RING_PD_CONTROL_REG, + DW9768_AAC_MODE_EN); + if (ret < 0) + return ret; + + /* Set AAC mode */ + ret = dw9768_mod_reg(dw9768, DW9768_AAC_PRESC_REG, + DW9768_AAC_MODE_SEL_MASK, + dw9768->aac_mode << 5); + if (ret < 0) + return ret; + + /* Set clock presc */ + if (dw9768->clock_presc != DW9768_CLOCK_PRE_SCALE_DEFAULT) { + ret = dw9768_mod_reg(dw9768, DW9768_AAC_PRESC_REG, + DW9768_CLOCK_PRE_SCALE_SEL_MASK, + dw9768->clock_presc); + if (ret < 0) + return ret; + } + + /* Set AAC Timing */ + if (dw9768->aac_timing != DW9768_AAC_TIME_DEFAULT) { + ret = i2c_smbus_write_byte_data(client, DW9768_AAC_TIME_REG, + dw9768->aac_timing); + if (ret < 0) + return ret; + } + + for (val = dw9768->focus->val % DW9768_MOVE_STEPS; + val <= dw9768->focus->val; + val += DW9768_MOVE_STEPS) { + ret = dw9768_set_dac(dw9768, val); + if (ret) { + dev_err(&client->dev, "I2C failure: %d", ret); + return ret; + } + usleep_range(dw9768->move_delay_us, + dw9768->move_delay_us + 1000); + } + + return 0; +} + +static int dw9768_release(struct dw9768 *dw9768) +{ + struct i2c_client *client = v4l2_get_subdevdata(&dw9768->sd); + int ret, val; + + val = round_down(dw9768->focus->val, DW9768_MOVE_STEPS); + for ( ; val >= 0; val -= DW9768_MOVE_STEPS) { + ret = dw9768_set_dac(dw9768, val); + if (ret) { + dev_err(&client->dev, "I2C write fail: %d", ret); + return ret; + } + usleep_range(dw9768->move_delay_us, + dw9768->move_delay_us + 1000); + } + + ret = i2c_smbus_write_byte_data(client, DW9768_RING_PD_CONTROL_REG, + DW9768_PD_MODE_EN); + if (ret < 0) + return ret; + + /* + * DW9769 requires waiting delay time of t_OPR + * after PD reset takes place. + */ + usleep_range(DW9768_T_OPR_US, DW9768_T_OPR_US + 100); + + return 0; +} + +static int dw9768_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct dw9768 *dw9768 = sd_to_dw9768(sd); + + dw9768_release(dw9768); + regulator_bulk_disable(ARRAY_SIZE(dw9768_supply_names), + dw9768->supplies); + + return 0; +} + +static int dw9768_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct dw9768 *dw9768 = sd_to_dw9768(sd); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(dw9768_supply_names), + dw9768->supplies); + if (ret < 0) { + dev_err(dev, "failed to enable regulators\n"); + return ret; + } + + /* + * The datasheet refers to t_OPR that needs to be waited before sending + * I2C commands after power-up. + */ + usleep_range(DW9768_T_OPR_US, DW9768_T_OPR_US + 100); + + ret = dw9768_init(dw9768); + if (ret < 0) + goto disable_regulator; + + return 0; + +disable_regulator: + regulator_bulk_disable(ARRAY_SIZE(dw9768_supply_names), + dw9768->supplies); + + return ret; +} + +static int dw9768_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct dw9768 *dw9768 = container_of(ctrl->handler, + struct dw9768, ctrls); + + if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) + return dw9768_set_dac(dw9768, ctrl->val); + + return 0; +} + +static const struct v4l2_ctrl_ops dw9768_ctrl_ops = { + .s_ctrl = dw9768_set_ctrl, +}; + +static int dw9768_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + int ret; + + ret = pm_runtime_get_sync(sd->dev); + if (ret < 0) { + pm_runtime_put_noidle(sd->dev); + return ret; + } + + return 0; +} + +static int dw9768_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + pm_runtime_put(sd->dev); + + return 0; +} + +static const struct v4l2_subdev_internal_ops dw9768_int_ops = { + .open = dw9768_open, + .close = dw9768_close, +}; + +static const struct v4l2_subdev_ops dw9768_ops = { }; + +static int dw9768_init_controls(struct dw9768 *dw9768) +{ + struct v4l2_ctrl_handler *hdl = &dw9768->ctrls; + const struct v4l2_ctrl_ops *ops = &dw9768_ctrl_ops; + + v4l2_ctrl_handler_init(hdl, 1); + + dw9768->focus = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_ABSOLUTE, 0, + DW9768_MAX_FOCUS_POS, + DW9768_FOCUS_STEPS, 0); + + if (hdl->error) + return hdl->error; + + dw9768->sd.ctrl_handler = hdl; + + return 0; +} + +static int dw9768_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct dw9768 *dw9768; + unsigned int i; + int ret; + + dw9768 = devm_kzalloc(dev, sizeof(*dw9768), GFP_KERNEL); + if (!dw9768) + return -ENOMEM; + + /* Initialize subdev */ + v4l2_i2c_subdev_init(&dw9768->sd, client, &dw9768_ops); + + dw9768->aac_mode = DW9768_AAC_MODE_DEFAULT; + dw9768->aac_timing = DW9768_AAC_TIME_DEFAULT; + dw9768->clock_presc = DW9768_CLOCK_PRE_SCALE_DEFAULT; + + /* Optional indication of AAC mode select */ + fwnode_property_read_u32(dev_fwnode(dev), "dongwoon,aac-mode", + &dw9768->aac_mode); + + /* Optional indication of clock pre-scale select */ + fwnode_property_read_u32(dev_fwnode(dev), "dongwoon,clock-presc", + &dw9768->clock_presc); + + /* Optional indication of AAC Timing */ + fwnode_property_read_u32(dev_fwnode(dev), "dongwoon,aac-timing", + &dw9768->aac_timing); + + dw9768->move_delay_us = dw9768_cal_move_delay(dw9768->aac_mode, + dw9768->clock_presc, + dw9768->aac_timing); + + for (i = 0; i < ARRAY_SIZE(dw9768_supply_names); i++) + dw9768->supplies[i].supply = dw9768_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(dw9768_supply_names), + dw9768->supplies); + if (ret) { + dev_err(dev, "failed to get regulators\n"); + return ret; + } + + /* Initialize controls */ + ret = dw9768_init_controls(dw9768); + if (ret) + goto err_free_handler; + + /* Initialize subdev */ + dw9768->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + dw9768->sd.internal_ops = &dw9768_int_ops; + + ret = media_entity_pads_init(&dw9768->sd.entity, 0, NULL); + if (ret < 0) + goto err_free_handler; + + dw9768->sd.entity.function = MEDIA_ENT_F_LENS; + + pm_runtime_enable(dev); + if (!pm_runtime_enabled(dev)) { + ret = dw9768_runtime_resume(dev); + if (ret < 0) { + dev_err(dev, "failed to power on: %d\n", ret); + goto err_clean_entity; + } + } + + ret = v4l2_async_register_subdev(&dw9768->sd); + if (ret < 0) { + dev_err(dev, "failed to register V4L2 subdev: %d", ret); + goto err_power_off; + } + + return 0; + +err_power_off: + if (pm_runtime_enabled(dev)) + pm_runtime_disable(dev); + else + dw9768_runtime_suspend(dev); +err_clean_entity: + media_entity_cleanup(&dw9768->sd.entity); +err_free_handler: + v4l2_ctrl_handler_free(&dw9768->ctrls); + + return ret; +} + +static int dw9768_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct dw9768 *dw9768 = sd_to_dw9768(sd); + + v4l2_async_unregister_subdev(&dw9768->sd); + v4l2_ctrl_handler_free(&dw9768->ctrls); + media_entity_cleanup(&dw9768->sd.entity); + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + dw9768_runtime_suspend(&client->dev); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +static const struct of_device_id dw9768_of_table[] = { + { .compatible = "dongwoon,dw9768" }, + { .compatible = "giantec,gt9769" }, + {} +}; +MODULE_DEVICE_TABLE(of, dw9768_of_table); + +static const struct dev_pm_ops dw9768_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(dw9768_runtime_suspend, dw9768_runtime_resume, NULL) +}; + +static struct i2c_driver dw9768_i2c_driver = { + .driver = { + .name = DW9768_NAME, + .pm = &dw9768_pm_ops, + .of_match_table = dw9768_of_table, + }, + .probe_new = dw9768_probe, + .remove = dw9768_remove, +}; +module_i2c_driver(dw9768_i2c_driver); + +MODULE_AUTHOR("Dongchun Zhu "); +MODULE_DESCRIPTION("DW9768 VCM driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 66d8c9d2422da21ed41f75c03ba0685987b65fe0 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Fri, 12 Jun 2020 16:47:11 +0200 Subject: media: i2c: Add MAX9286 driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MAX9286 is a 4-channel GMSL deserializer with coax or STP input and CSI-2 output. The device supports multicamera streaming applications, and features the ability to synchronise the attached cameras. CSI-2 output can be configured with 1 to 4 lanes, and a control channel is supported over I2C, which implements an I2C mux to facilitate communications with connected cameras across the reverse control channel. Signed-off-by: Jacopo Mondi Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart Signed-off-by: Niklas Söderlund Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 10 + drivers/media/i2c/Kconfig | 13 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/max9286.c | 1320 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1344 insertions(+) create mode 100644 drivers/media/i2c/max9286.c (limited to 'drivers/media') diff --git a/MAINTAINERS b/MAINTAINERS index dc28f18c62bd..3bcedb12ab92 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10438,6 +10438,16 @@ F: Documentation/hwmon/max6697.rst F: drivers/hwmon/max6697.c F: include/linux/platform_data/max6697.h +MAX9286 QUAD GMSL DESERIALIZER DRIVER +M: Jacopo Mondi +M: Kieran Bingham +M: Laurent Pinchart +M: Niklas Söderlund +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/i2c/maxim,max9286.yaml +F: drivers/media/i2c/max9286.c + MAX9860 MONO AUDIO VOICE CODEC DRIVER M: Peter Rosin L: alsa-devel@alsa-project.org (moderated for non-subscribers) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index d2d86ced319d..8471a6c438d4 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -464,6 +464,19 @@ config VIDEO_VPX3220 To compile this driver as a module, choose M here: the module will be called vpx3220. +config VIDEO_MAX9286 + tristate "Maxim MAX9286 GMSL deserializer support" + depends on I2C && I2C_MUX + depends on OF + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + select MEDIA_CONTROLLER + help + This driver supports the Maxim MAX9286 GMSL deserializer. + + To compile this driver as a module, choose M here: the + module will be called max9286. + comment "Video and audio decoders" config VIDEO_SAA717X diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 33ad435bffea..9091f4918d4b 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -119,6 +119,7 @@ obj-$(CONFIG_VIDEO_IMX274) += imx274.o obj-$(CONFIG_VIDEO_IMX290) += imx290.o obj-$(CONFIG_VIDEO_IMX319) += imx319.o obj-$(CONFIG_VIDEO_IMX355) += imx355.o +obj-$(CONFIG_VIDEO_MAX9286) += max9286.o obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o obj-$(CONFIG_SDR_MAX2175) += max2175.o diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c new file mode 100644 index 000000000000..47f280518fdb --- /dev/null +++ b/drivers/media/i2c/max9286.c @@ -0,0 +1,1320 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Maxim MAX9286 GMSL Deserializer Driver + * + * Copyright (C) 2017-2019 Jacopo Mondi + * Copyright (C) 2017-2019 Kieran Bingham + * Copyright (C) 2017-2019 Laurent Pinchart + * Copyright (C) 2017-2019 Niklas Söderlund + * Copyright (C) 2016 Renesas Electronics Corporation + * Copyright (C) 2015 Cogent Embedded, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Register 0x00 */ +#define MAX9286_MSTLINKSEL_AUTO (7 << 5) +#define MAX9286_MSTLINKSEL(n) ((n) << 5) +#define MAX9286_EN_VS_GEN BIT(4) +#define MAX9286_LINKEN(n) (1 << (n)) +/* Register 0x01 */ +#define MAX9286_FSYNCMODE_ECU (3 << 6) +#define MAX9286_FSYNCMODE_EXT (2 << 6) +#define MAX9286_FSYNCMODE_INT_OUT (1 << 6) +#define MAX9286_FSYNCMODE_INT_HIZ (0 << 6) +#define MAX9286_GPIEN BIT(5) +#define MAX9286_ENLMO_RSTFSYNC BIT(2) +#define MAX9286_FSYNCMETH_AUTO (2 << 0) +#define MAX9286_FSYNCMETH_SEMI_AUTO (1 << 0) +#define MAX9286_FSYNCMETH_MANUAL (0 << 0) +#define MAX9286_REG_FSYNC_PERIOD_L 0x06 +#define MAX9286_REG_FSYNC_PERIOD_M 0x07 +#define MAX9286_REG_FSYNC_PERIOD_H 0x08 +/* Register 0x0a */ +#define MAX9286_FWDCCEN(n) (1 << ((n) + 4)) +#define MAX9286_REVCCEN(n) (1 << (n)) +/* Register 0x0c */ +#define MAX9286_HVEN BIT(7) +#define MAX9286_EDC_6BIT_HAMMING (2 << 5) +#define MAX9286_EDC_6BIT_CRC (1 << 5) +#define MAX9286_EDC_1BIT_PARITY (0 << 5) +#define MAX9286_DESEL BIT(4) +#define MAX9286_INVVS BIT(3) +#define MAX9286_INVHS BIT(2) +#define MAX9286_HVSRC_D0 (2 << 0) +#define MAX9286_HVSRC_D14 (1 << 0) +#define MAX9286_HVSRC_D18 (0 << 0) +/* Register 0x0f */ +#define MAX9286_0X0F_RESERVED BIT(3) +/* Register 0x12 */ +#define MAX9286_CSILANECNT(n) (((n) - 1) << 6) +#define MAX9286_CSIDBL BIT(5) +#define MAX9286_DBL BIT(4) +#define MAX9286_DATATYPE_USER_8BIT (11 << 0) +#define MAX9286_DATATYPE_USER_YUV_12BIT (10 << 0) +#define MAX9286_DATATYPE_USER_24BIT (9 << 0) +#define MAX9286_DATATYPE_RAW14 (8 << 0) +#define MAX9286_DATATYPE_RAW11 (7 << 0) +#define MAX9286_DATATYPE_RAW10 (6 << 0) +#define MAX9286_DATATYPE_RAW8 (5 << 0) +#define MAX9286_DATATYPE_YUV422_10BIT (4 << 0) +#define MAX9286_DATATYPE_YUV422_8BIT (3 << 0) +#define MAX9286_DATATYPE_RGB555 (2 << 0) +#define MAX9286_DATATYPE_RGB565 (1 << 0) +#define MAX9286_DATATYPE_RGB888 (0 << 0) +/* Register 0x15 */ +#define MAX9286_VC(n) ((n) << 5) +#define MAX9286_VCTYPE BIT(4) +#define MAX9286_CSIOUTEN BIT(3) +#define MAX9286_0X15_RESV (3 << 0) +/* Register 0x1b */ +#define MAX9286_SWITCHIN(n) (1 << ((n) + 4)) +#define MAX9286_ENEQ(n) (1 << (n)) +/* Register 0x27 */ +#define MAX9286_LOCKED BIT(7) +/* Register 0x31 */ +#define MAX9286_FSYNC_LOCKED BIT(6) +/* Register 0x34 */ +#define MAX9286_I2CLOCACK BIT(7) +#define MAX9286_I2CSLVSH_1046NS_469NS (3 << 5) +#define MAX9286_I2CSLVSH_938NS_352NS (2 << 5) +#define MAX9286_I2CSLVSH_469NS_234NS (1 << 5) +#define MAX9286_I2CSLVSH_352NS_117NS (0 << 5) +#define MAX9286_I2CMSTBT_837KBPS (7 << 2) +#define MAX9286_I2CMSTBT_533KBPS (6 << 2) +#define MAX9286_I2CMSTBT_339KBPS (5 << 2) +#define MAX9286_I2CMSTBT_173KBPS (4 << 2) +#define MAX9286_I2CMSTBT_105KBPS (3 << 2) +#define MAX9286_I2CMSTBT_84KBPS (2 << 2) +#define MAX9286_I2CMSTBT_28KBPS (1 << 2) +#define MAX9286_I2CMSTBT_8KBPS (0 << 2) +#define MAX9286_I2CSLVTO_NONE (3 << 0) +#define MAX9286_I2CSLVTO_1024US (2 << 0) +#define MAX9286_I2CSLVTO_256US (1 << 0) +#define MAX9286_I2CSLVTO_64US (0 << 0) +/* Register 0x3b */ +#define MAX9286_REV_TRF(n) ((n) << 4) +#define MAX9286_REV_AMP(n) ((((n) - 30) / 10) << 1) /* in mV */ +#define MAX9286_REV_AMP_X BIT(0) +/* Register 0x3f */ +#define MAX9286_EN_REV_CFG BIT(6) +#define MAX9286_REV_FLEN(n) ((n) - 20) +/* Register 0x49 */ +#define MAX9286_VIDEO_DETECT_MASK 0x0f +/* Register 0x69 */ +#define MAX9286_LFLTBMONMASKED BIT(7) +#define MAX9286_LOCKMONMASKED BIT(6) +#define MAX9286_AUTOCOMBACKEN BIT(5) +#define MAX9286_AUTOMASKEN BIT(4) +#define MAX9286_MASKLINK(n) ((n) << 0) + +/* + * The sink and source pads are created to match the OF graph port numbers so + * that their indexes can be used interchangeably. + */ +#define MAX9286_NUM_GMSL 4 +#define MAX9286_N_SINKS 4 +#define MAX9286_N_PADS 5 +#define MAX9286_SRC_PAD 4 + +struct max9286_source { + struct v4l2_async_subdev asd; + struct v4l2_subdev *sd; + struct fwnode_handle *fwnode; +}; + +#define asd_to_max9286_source(_asd) \ + container_of(_asd, struct max9286_source, asd) + +struct max9286_priv { + struct i2c_client *client; + struct gpio_desc *gpiod_pwdn; + struct v4l2_subdev sd; + struct media_pad pads[MAX9286_N_PADS]; + struct regulator *regulator; + + struct gpio_chip gpio; + u8 gpio_state; + + struct i2c_mux_core *mux; + unsigned int mux_channel; + bool mux_open; + + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *pixelrate; + + struct v4l2_mbus_framefmt fmt[MAX9286_N_SINKS]; + + /* Protects controls and fmt structures */ + struct mutex mutex; + + unsigned int nsources; + unsigned int source_mask; + unsigned int route_mask; + unsigned int bound_sources; + unsigned int csi2_data_lanes; + struct max9286_source sources[MAX9286_NUM_GMSL]; + struct v4l2_async_notifier notifier; +}; + +static struct max9286_source *next_source(struct max9286_priv *priv, + struct max9286_source *source) +{ + if (!source) + source = &priv->sources[0]; + else + source++; + + for (; source < &priv->sources[MAX9286_NUM_GMSL]; source++) { + if (source->fwnode) + return source; + } + + return NULL; +} + +#define for_each_source(priv, source) \ + for ((source) = NULL; ((source) = next_source((priv), (source))); ) + +#define to_index(priv, source) ((source) - &(priv)->sources[0]) + +static inline struct max9286_priv *sd_to_max9286(struct v4l2_subdev *sd) +{ + return container_of(sd, struct max9286_priv, sd); +} + +/* ----------------------------------------------------------------------------- + * I2C IO + */ + +static int max9286_read(struct max9286_priv *priv, u8 reg) +{ + int ret; + + ret = i2c_smbus_read_byte_data(priv->client, reg); + if (ret < 0) + dev_err(&priv->client->dev, + "%s: register 0x%02x read failed (%d)\n", + __func__, reg, ret); + + return ret; +} + +static int max9286_write(struct max9286_priv *priv, u8 reg, u8 val) +{ + int ret; + + ret = i2c_smbus_write_byte_data(priv->client, reg, val); + if (ret < 0) + dev_err(&priv->client->dev, + "%s: register 0x%02x write failed (%d)\n", + __func__, reg, ret); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * I2C Multiplexer + */ + +static void max9286_i2c_mux_configure(struct max9286_priv *priv, u8 conf) +{ + max9286_write(priv, 0x0a, conf); + + /* + * We must sleep after any change to the forward or reverse channel + * configuration. + */ + usleep_range(3000, 5000); +} + +static void max9286_i2c_mux_open(struct max9286_priv *priv) +{ + /* Open all channels on the MAX9286 */ + max9286_i2c_mux_configure(priv, 0xff); + + priv->mux_open = true; +} + +static void max9286_i2c_mux_close(struct max9286_priv *priv) +{ + /* + * Ensure that both the forward and reverse channel are disabled on the + * mux, and that the channel ID is invalidated to ensure we reconfigure + * on the next max9286_i2c_mux_select() call. + */ + max9286_i2c_mux_configure(priv, 0x00); + + priv->mux_open = false; + priv->mux_channel = -1; +} + +static int max9286_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan) +{ + struct max9286_priv *priv = i2c_mux_priv(muxc); + + /* Channel select is disabled when configured in the opened state. */ + if (priv->mux_open) + return 0; + + if (priv->mux_channel == chan) + return 0; + + priv->mux_channel = chan; + + max9286_i2c_mux_configure(priv, + MAX9286_FWDCCEN(chan) | + MAX9286_REVCCEN(chan)); + + return 0; +} + +static int max9286_i2c_mux_init(struct max9286_priv *priv) +{ + struct max9286_source *source; + int ret; + + if (!i2c_check_functionality(priv->client->adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -ENODEV; + + priv->mux = i2c_mux_alloc(priv->client->adapter, &priv->client->dev, + priv->nsources, 0, I2C_MUX_LOCKED, + max9286_i2c_mux_select, NULL); + if (!priv->mux) + return -ENOMEM; + + priv->mux->priv = priv; + + for_each_source(priv, source) { + unsigned int index = to_index(priv, source); + + ret = i2c_mux_add_adapter(priv->mux, 0, index, 0); + if (ret < 0) + goto error; + } + + return 0; + +error: + i2c_mux_del_adapters(priv->mux); + return ret; +} + +static void max9286_configure_i2c(struct max9286_priv *priv, bool localack) +{ + u8 config = MAX9286_I2CSLVSH_469NS_234NS | MAX9286_I2CSLVTO_1024US | + MAX9286_I2CMSTBT_105KBPS; + + if (localack) + config |= MAX9286_I2CLOCACK; + + max9286_write(priv, 0x34, config); + usleep_range(3000, 5000); +} + +/* + * max9286_check_video_links() - Make sure video links are detected and locked + * + * Performs safety checks on video link status. Make sure they are detected + * and all enabled links are locked. + * + * Returns 0 for success, -EIO for errors. + */ +static int max9286_check_video_links(struct max9286_priv *priv) +{ + unsigned int i; + int ret; + + /* + * Make sure valid video links are detected. + * The delay is not characterized in de-serializer manual, wait up + * to 5 ms. + */ + for (i = 0; i < 10; i++) { + ret = max9286_read(priv, 0x49); + if (ret < 0) + return -EIO; + + if ((ret & MAX9286_VIDEO_DETECT_MASK) == priv->source_mask) + break; + + usleep_range(350, 500); + } + + if (i == 10) { + dev_err(&priv->client->dev, + "Unable to detect video links: 0x%02x\n", ret); + return -EIO; + } + + /* Make sure all enabled links are locked (4ms max). */ + for (i = 0; i < 10; i++) { + ret = max9286_read(priv, 0x27); + if (ret < 0) + return -EIO; + + if (ret & MAX9286_LOCKED) + break; + + usleep_range(350, 450); + } + + if (i == 10) { + dev_err(&priv->client->dev, "Not all enabled links locked\n"); + return -EIO; + } + + return 0; +} + +/* + * max9286_check_config_link() - Detect and wait for configuration links + * + * Determine if the configuration channel is up and settled for a link. + * + * Returns 0 for success, -EIO for errors. + */ +static int max9286_check_config_link(struct max9286_priv *priv, + unsigned int source_mask) +{ + unsigned int conflink_mask = (source_mask & 0x0f) << 4; + unsigned int i; + int ret; + + /* + * Make sure requested configuration links are detected. + * The delay is not characterized in the chip manual: wait up + * to 5 milliseconds. + */ + for (i = 0; i < 10; i++) { + ret = max9286_read(priv, 0x49) & 0xf0; + if (ret < 0) + return -EIO; + + if (ret == conflink_mask) + break; + + usleep_range(350, 500); + } + + if (ret != conflink_mask) { + dev_err(&priv->client->dev, + "Unable to detect configuration links: 0x%02x expected 0x%02x\n", + ret, conflink_mask); + return -EIO; + } + + dev_info(&priv->client->dev, + "Successfully detected configuration links after %u loops: 0x%02x\n", + i, conflink_mask); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdev + */ + +static int max9286_set_pixelrate(struct max9286_priv *priv) +{ + struct max9286_source *source = NULL; + u64 pixelrate = 0; + + for_each_source(priv, source) { + struct v4l2_ctrl *ctrl; + u64 source_rate = 0; + + /* Pixel rate is mandatory to be reported by sources. */ + ctrl = v4l2_ctrl_find(source->sd->ctrl_handler, + V4L2_CID_PIXEL_RATE); + if (!ctrl) { + pixelrate = 0; + break; + } + + /* All source must report the same pixel rate. */ + source_rate = v4l2_ctrl_g_ctrl_int64(ctrl); + if (!pixelrate) { + pixelrate = source_rate; + } else if (pixelrate != source_rate) { + dev_err(&priv->client->dev, + "Unable to calculate pixel rate\n"); + return -EINVAL; + } + } + + if (!pixelrate) { + dev_err(&priv->client->dev, + "No pixel rate control available in sources\n"); + return -EINVAL; + } + + /* + * The CSI-2 transmitter pixel rate is the single source rate multiplied + * by the number of available sources. + */ + return v4l2_ctrl_s_ctrl_int64(priv->pixelrate, + pixelrate * priv->nsources); +} + +static int max9286_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct max9286_priv *priv = sd_to_max9286(notifier->sd); + struct max9286_source *source = asd_to_max9286_source(asd); + unsigned int index = to_index(priv, source); + unsigned int src_pad; + int ret; + + ret = media_entity_get_fwnode_pad(&subdev->entity, + source->fwnode, + MEDIA_PAD_FL_SOURCE); + if (ret < 0) { + dev_err(&priv->client->dev, + "Failed to find pad for %s\n", subdev->name); + return ret; + } + + priv->bound_sources |= BIT(index); + source->sd = subdev; + src_pad = ret; + + ret = media_create_pad_link(&source->sd->entity, src_pad, + &priv->sd.entity, index, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(&priv->client->dev, + "Unable to link %s:%u -> %s:%u\n", + source->sd->name, src_pad, priv->sd.name, index); + return ret; + } + + dev_dbg(&priv->client->dev, "Bound %s pad: %u on index %u\n", + subdev->name, src_pad, index); + + /* + * We can only register v4l2_async_notifiers, which do not provide a + * means to register a complete callback. bound_sources allows us to + * identify when all remote serializers have completed their probe. + */ + if (priv->bound_sources != priv->source_mask) + return 0; + + /* + * All enabled sources have probed and enabled their reverse control + * channels: + * + * - Verify all configuration links are properly detected + * - Disable auto-ack as communication on the control channel are now + * stable. + */ + max9286_check_config_link(priv, priv->source_mask); + + /* + * Re-configure I2C with local acknowledge disabled after cameras have + * probed. + */ + max9286_configure_i2c(priv, false); + + return max9286_set_pixelrate(priv); +} + +static void max9286_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct max9286_priv *priv = sd_to_max9286(notifier->sd); + struct max9286_source *source = asd_to_max9286_source(asd); + unsigned int index = to_index(priv, source); + + source->sd = NULL; + priv->bound_sources &= ~BIT(index); +} + +static const struct v4l2_async_notifier_operations max9286_notify_ops = { + .bound = max9286_notify_bound, + .unbind = max9286_notify_unbind, +}; + +static int max9286_v4l2_notifier_register(struct max9286_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct max9286_source *source = NULL; + int ret; + + if (!priv->nsources) + return 0; + + v4l2_async_notifier_init(&priv->notifier); + + for_each_source(priv, source) { + unsigned int i = to_index(priv, source); + + source->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + source->asd.match.fwnode = source->fwnode; + + ret = v4l2_async_notifier_add_subdev(&priv->notifier, + &source->asd); + if (ret) { + dev_err(dev, "Failed to add subdev for source %d", i); + v4l2_async_notifier_cleanup(&priv->notifier); + return ret; + } + + /* + * Balance the reference counting handled through + * v4l2_async_notifier_cleanup() + */ + fwnode_handle_get(source->fwnode); + } + + priv->notifier.ops = &max9286_notify_ops; + + ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier); + if (ret) { + dev_err(dev, "Failed to register subdev_notifier"); + v4l2_async_notifier_cleanup(&priv->notifier); + return ret; + } + + return 0; +} + +static void max9286_v4l2_notifier_unregister(struct max9286_priv *priv) +{ + if (!priv->nsources) + return; + + v4l2_async_notifier_unregister(&priv->notifier); + v4l2_async_notifier_cleanup(&priv->notifier); +} + +static int max9286_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct max9286_priv *priv = sd_to_max9286(sd); + struct max9286_source *source; + unsigned int i; + bool sync = false; + int ret; + + if (enable) { + /* + * The frame sync between cameras is transmitted across the + * reverse channel as GPIO. We must open all channels while + * streaming to allow this synchronisation signal to be shared. + */ + max9286_i2c_mux_open(priv); + + /* Start all cameras. */ + for_each_source(priv, source) { + ret = v4l2_subdev_call(source->sd, video, s_stream, 1); + if (ret) + return ret; + } + + ret = max9286_check_video_links(priv); + if (ret) + return ret; + + /* + * Wait until frame synchronization is locked. + * + * Manual says frame sync locking should take ~6 VTS. + * From practical experience at least 8 are required. Give + * 12 complete frames time (~400ms at 30 fps) to achieve frame + * locking before returning error. + */ + for (i = 0; i < 40; i++) { + if (max9286_read(priv, 0x31) & MAX9286_FSYNC_LOCKED) { + sync = true; + break; + } + usleep_range(9000, 11000); + } + + if (!sync) { + dev_err(&priv->client->dev, + "Failed to get frame synchronization\n"); + return -EXDEV; /* Invalid cross-device link */ + } + + /* + * Enable CSI output, VC set according to link number. + * Bit 7 must be set (chip manual says it's 0 and reserved). + */ + max9286_write(priv, 0x15, 0x80 | MAX9286_VCTYPE | + MAX9286_CSIOUTEN | MAX9286_0X15_RESV); + } else { + max9286_write(priv, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV); + + /* Stop all cameras. */ + for_each_source(priv, source) + v4l2_subdev_call(source->sd, video, s_stream, 0); + + max9286_i2c_mux_close(priv); + } + + return 0; +} + +static int max9286_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_UYVY8_1X16; + + return 0; +} + +static struct v4l2_mbus_framefmt * +max9286_get_pad_format(struct max9286_priv *priv, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&priv->sd, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &priv->fmt[pad]; + default: + return NULL; + } +} + +static int max9286_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct max9286_priv *priv = sd_to_max9286(sd); + struct v4l2_mbus_framefmt *cfg_fmt; + + if (format->pad == MAX9286_SRC_PAD) + return -EINVAL; + + /* Refuse non YUV422 formats as we hardcode DT to 8 bit YUV422 */ + switch (format->format.code) { + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_VYUY8_1X16: + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_YVYU8_1X16: + break; + default: + format->format.code = MEDIA_BUS_FMT_UYVY8_1X16; + break; + } + + cfg_fmt = max9286_get_pad_format(priv, cfg, format->pad, format->which); + if (!cfg_fmt) + return -EINVAL; + + mutex_lock(&priv->mutex); + *cfg_fmt = format->format; + mutex_unlock(&priv->mutex); + + return 0; +} + +static int max9286_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct max9286_priv *priv = sd_to_max9286(sd); + struct v4l2_mbus_framefmt *cfg_fmt; + unsigned int pad = format->pad; + + /* + * Multiplexed Stream Support: Support link validation by returning the + * format of the first bound link. All links must have the same format, + * as we do not support mixing and matching of cameras connected to the + * max9286. + */ + if (pad == MAX9286_SRC_PAD) + pad = __ffs(priv->bound_sources); + + cfg_fmt = max9286_get_pad_format(priv, cfg, pad, format->which); + if (!cfg_fmt) + return -EINVAL; + + mutex_lock(&priv->mutex); + format->format = *cfg_fmt; + mutex_unlock(&priv->mutex); + + return 0; +} + +static const struct v4l2_subdev_video_ops max9286_video_ops = { + .s_stream = max9286_s_stream, +}; + +static const struct v4l2_subdev_pad_ops max9286_pad_ops = { + .enum_mbus_code = max9286_enum_mbus_code, + .get_fmt = max9286_get_fmt, + .set_fmt = max9286_set_fmt, +}; + +static const struct v4l2_subdev_ops max9286_subdev_ops = { + .video = &max9286_video_ops, + .pad = &max9286_pad_ops, +}; + +static void max9286_init_format(struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = 1280; + fmt->height = 800; + fmt->code = MEDIA_BUS_FMT_UYVY8_1X16; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->field = V4L2_FIELD_NONE; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt->quantization = V4L2_QUANTIZATION_DEFAULT; + fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static int max9286_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *format; + unsigned int i; + + for (i = 0; i < MAX9286_N_SINKS; i++) { + format = v4l2_subdev_get_try_format(subdev, fh->pad, i); + max9286_init_format(format); + } + + return 0; +} + +static const struct v4l2_subdev_internal_ops max9286_subdev_internal_ops = { + .open = max9286_open, +}; + +static int max9286_s_ctrl(struct v4l2_ctrl *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_PIXEL_RATE: + return 0; + default: + return -EINVAL; + } +} + +static const struct v4l2_ctrl_ops max9286_ctrl_ops = { + .s_ctrl = max9286_s_ctrl, +}; + +static int max9286_v4l2_register(struct max9286_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct fwnode_handle *ep; + int ret; + int i; + + /* Register v4l2 async notifiers for connected Camera subdevices */ + ret = max9286_v4l2_notifier_register(priv); + if (ret) { + dev_err(dev, "Unable to register V4L2 async notifiers\n"); + return ret; + } + + /* Configure V4L2 for the MAX9286 itself */ + + for (i = 0; i < MAX9286_N_SINKS; i++) + max9286_init_format(&priv->fmt[i]); + + v4l2_i2c_subdev_init(&priv->sd, priv->client, &max9286_subdev_ops); + priv->sd.internal_ops = &max9286_subdev_internal_ops; + priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + v4l2_ctrl_handler_init(&priv->ctrls, 1); + priv->pixelrate = v4l2_ctrl_new_std(&priv->ctrls, + &max9286_ctrl_ops, + V4L2_CID_PIXEL_RATE, + 1, INT_MAX, 1, 50000000); + + priv->sd.ctrl_handler = &priv->ctrls; + ret = priv->ctrls.error; + if (ret) + goto err_async; + + priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + + priv->pads[MAX9286_SRC_PAD].flags = MEDIA_PAD_FL_SOURCE; + for (i = 0; i < MAX9286_SRC_PAD; i++) + priv->pads[i].flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&priv->sd.entity, MAX9286_N_PADS, + priv->pads); + if (ret) + goto err_async; + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), MAX9286_SRC_PAD, + 0, 0); + if (!ep) { + dev_err(dev, "Unable to retrieve endpoint on \"port@4\"\n"); + ret = -ENOENT; + goto err_async; + } + priv->sd.fwnode = ep; + + ret = v4l2_async_register_subdev(&priv->sd); + if (ret < 0) { + dev_err(dev, "Unable to register subdevice\n"); + goto err_put_node; + } + + return 0; + +err_put_node: + fwnode_handle_put(ep); +err_async: + max9286_v4l2_notifier_unregister(priv); + + return ret; +} + +static void max9286_v4l2_unregister(struct max9286_priv *priv) +{ + fwnode_handle_put(priv->sd.fwnode); + v4l2_async_unregister_subdev(&priv->sd); + max9286_v4l2_notifier_unregister(priv); +} + +/* ----------------------------------------------------------------------------- + * Probe/Remove + */ + +static int max9286_setup(struct max9286_priv *priv) +{ + /* + * Link ordering values for all enabled links combinations. Orders must + * be assigned sequentially from 0 to the number of enabled links + * without leaving any hole for disabled links. We thus assign orders to + * enabled links first, and use the remaining order values for disabled + * links are all links must have a different order value; + */ + static const u8 link_order[] = { + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxxx */ + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxx0 */ + (3 << 6) | (2 << 4) | (0 << 2) | (1 << 0), /* xx0x */ + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xx10 */ + (3 << 6) | (0 << 4) | (2 << 2) | (1 << 0), /* x0xx */ + (3 << 6) | (1 << 4) | (2 << 2) | (0 << 0), /* x1x0 */ + (3 << 6) | (1 << 4) | (0 << 2) | (2 << 0), /* x10x */ + (3 << 6) | (1 << 4) | (1 << 2) | (0 << 0), /* x210 */ + (0 << 6) | (3 << 4) | (2 << 2) | (1 << 0), /* 0xxx */ + (1 << 6) | (3 << 4) | (2 << 2) | (0 << 0), /* 1xx0 */ + (1 << 6) | (3 << 4) | (0 << 2) | (2 << 0), /* 1x0x */ + (2 << 6) | (3 << 4) | (1 << 2) | (0 << 0), /* 2x10 */ + (1 << 6) | (0 << 4) | (3 << 2) | (2 << 0), /* 10xx */ + (2 << 6) | (1 << 4) | (3 << 2) | (0 << 0), /* 21x0 */ + (2 << 6) | (1 << 4) | (0 << 2) | (3 << 0), /* 210x */ + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* 3210 */ + }; + + /* + * Set the I2C bus speed. + * + * Enable I2C Local Acknowledge during the probe sequences of the camera + * only. This should be disabled after the mux is initialised. + */ + max9286_configure_i2c(priv, true); + + /* + * Reverse channel setup. + * + * - Enable custom reverse channel configuration (through register 0x3f) + * and set the first pulse length to 35 clock cycles. + * - Increase the reverse channel amplitude to 170mV to accommodate the + * high threshold enabled by the serializer driver. + */ + max9286_write(priv, 0x3f, MAX9286_EN_REV_CFG | MAX9286_REV_FLEN(35)); + max9286_write(priv, 0x3b, MAX9286_REV_TRF(1) | MAX9286_REV_AMP(70) | + MAX9286_REV_AMP_X); + usleep_range(2000, 2500); + + /* + * Enable GMSL links, mask unused ones and autodetect link + * used as CSI clock source. + */ + max9286_write(priv, 0x00, MAX9286_MSTLINKSEL_AUTO | priv->route_mask); + max9286_write(priv, 0x0b, link_order[priv->route_mask]); + max9286_write(priv, 0x69, (0xf & ~priv->route_mask)); + + /* + * Video format setup: + * Disable CSI output, VC is set according to Link number. + */ + max9286_write(priv, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV); + + /* Enable CSI-2 Lane D0-D3 only, DBL mode, YUV422 8-bit. */ + max9286_write(priv, 0x12, MAX9286_CSIDBL | MAX9286_DBL | + MAX9286_CSILANECNT(priv->csi2_data_lanes) | + MAX9286_DATATYPE_YUV422_8BIT); + + /* Automatic: FRAMESYNC taken from the slowest Link. */ + max9286_write(priv, 0x01, MAX9286_FSYNCMODE_INT_HIZ | + MAX9286_FSYNCMETH_AUTO); + + /* Enable HS/VS encoding, use D14/15 for HS/VS, invert VS. */ + max9286_write(priv, 0x0c, MAX9286_HVEN | MAX9286_INVVS | + MAX9286_HVSRC_D14); + + /* + * The overlap window seems to provide additional validation by tracking + * the delay between vsync and frame sync, generating an error if the + * delay is bigger than the programmed window, though it's not yet clear + * what value should be set. + * + * As it's an optional value and can be disabled, we do so by setting + * a 0 overlap value. + */ + max9286_write(priv, 0x63, 0); + max9286_write(priv, 0x64, 0); + + /* + * Wait for 2ms to allow the link to resynchronize after the + * configuration change. + */ + usleep_range(2000, 5000); + + return 0; +} + +static void max9286_gpio_set(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct max9286_priv *priv = gpiochip_get_data(chip); + + if (value) + priv->gpio_state |= BIT(offset); + else + priv->gpio_state &= ~BIT(offset); + + max9286_write(priv, 0x0f, MAX9286_0X0F_RESERVED | priv->gpio_state); +} + +static int max9286_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct max9286_priv *priv = gpiochip_get_data(chip); + + return priv->gpio_state & BIT(offset); +} + +static int max9286_register_gpio(struct max9286_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct gpio_chip *gpio = &priv->gpio; + int ret; + + /* Configure the GPIO */ + gpio->label = dev_name(dev); + gpio->parent = dev; + gpio->owner = THIS_MODULE; + gpio->of_node = dev->of_node; + gpio->ngpio = 2; + gpio->base = -1; + gpio->set = max9286_gpio_set; + gpio->get = max9286_gpio_get; + gpio->can_sleep = true; + + /* GPIO values default to high */ + priv->gpio_state = BIT(0) | BIT(1); + + ret = devm_gpiochip_add_data(dev, gpio, priv); + if (ret) + dev_err(dev, "Unable to create gpio_chip\n"); + + return ret; +} + +static int max9286_init(struct device *dev) +{ + struct max9286_priv *priv; + struct i2c_client *client; + int ret; + + client = to_i2c_client(dev); + priv = i2c_get_clientdata(client); + + /* Enable the bus power. */ + ret = regulator_enable(priv->regulator); + if (ret < 0) { + dev_err(&client->dev, "Unable to turn PoC on\n"); + return ret; + } + + ret = max9286_setup(priv); + if (ret) { + dev_err(dev, "Unable to setup max9286\n"); + goto err_regulator; + } + + /* + * Register all V4L2 interactions for the MAX9286 and notifiers for + * any subdevices connected. + */ + ret = max9286_v4l2_register(priv); + if (ret) { + dev_err(dev, "Failed to register with V4L2\n"); + goto err_regulator; + } + + ret = max9286_i2c_mux_init(priv); + if (ret) { + dev_err(dev, "Unable to initialize I2C multiplexer\n"); + goto err_v4l2_register; + } + + /* Leave the mux channels disabled until they are selected. */ + max9286_i2c_mux_close(priv); + + return 0; + +err_v4l2_register: + max9286_v4l2_unregister(priv); +err_regulator: + regulator_disable(priv->regulator); + + return ret; +} + +static void max9286_cleanup_dt(struct max9286_priv *priv) +{ + struct max9286_source *source; + + for_each_source(priv, source) { + fwnode_handle_put(source->fwnode); + source->fwnode = NULL; + } +} + +static int max9286_parse_dt(struct max9286_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct device_node *i2c_mux; + struct device_node *node = NULL; + unsigned int i2c_mux_mask = 0; + + /* Balance the of_node_put() performed by of_find_node_by_name(). */ + of_node_get(dev->of_node); + i2c_mux = of_find_node_by_name(dev->of_node, "i2c-mux"); + if (!i2c_mux) { + dev_err(dev, "Failed to find i2c-mux node\n"); + return -EINVAL; + } + + /* Identify which i2c-mux channels are enabled */ + for_each_child_of_node(i2c_mux, node) { + u32 id = 0; + + of_property_read_u32(node, "reg", &id); + if (id >= MAX9286_NUM_GMSL) + continue; + + if (!of_device_is_available(node)) { + dev_dbg(dev, "Skipping disabled I2C bus port %u\n", id); + continue; + } + + i2c_mux_mask |= BIT(id); + } + of_node_put(node); + of_node_put(i2c_mux); + + /* Parse the endpoints */ + for_each_endpoint_of_node(dev->of_node, node) { + struct max9286_source *source; + struct of_endpoint ep; + + of_graph_parse_endpoint(node, &ep); + dev_dbg(dev, "Endpoint %pOF on port %d", + ep.local_node, ep.port); + + if (ep.port > MAX9286_NUM_GMSL) { + dev_err(dev, "Invalid endpoint %s on port %d", + of_node_full_name(ep.local_node), ep.port); + continue; + } + + /* For the source endpoint just parse the bus configuration. */ + if (ep.port == MAX9286_SRC_PAD) { + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + int ret; + + ret = v4l2_fwnode_endpoint_parse( + of_fwnode_handle(node), &vep); + if (ret) { + of_node_put(node); + return ret; + } + + priv->csi2_data_lanes = + vep.bus.mipi_csi2.num_data_lanes; + + continue; + } + + /* Skip if the corresponding GMSL link is unavailable. */ + if (!(i2c_mux_mask & BIT(ep.port))) + continue; + + if (priv->sources[ep.port].fwnode) { + dev_err(dev, + "Multiple port endpoints are not supported: %d", + ep.port); + + continue; + } + + source = &priv->sources[ep.port]; + source->fwnode = fwnode_graph_get_remote_endpoint( + of_fwnode_handle(node)); + if (!source->fwnode) { + dev_err(dev, + "Endpoint %pOF has no remote endpoint connection\n", + ep.local_node); + + continue; + } + + priv->source_mask |= BIT(ep.port); + priv->nsources++; + } + of_node_put(node); + + priv->route_mask = priv->source_mask; + + return 0; +} + +static int max9286_probe(struct i2c_client *client) +{ + struct max9286_priv *priv; + int ret; + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_init(&priv->mutex); + + priv->client = client; + i2c_set_clientdata(client, priv); + + priv->gpiod_pwdn = devm_gpiod_get_optional(&client->dev, "enable", + GPIOD_OUT_HIGH); + if (IS_ERR(priv->gpiod_pwdn)) + return PTR_ERR(priv->gpiod_pwdn); + + gpiod_set_consumer_name(priv->gpiod_pwdn, "max9286-pwdn"); + gpiod_set_value_cansleep(priv->gpiod_pwdn, 1); + + /* Wait at least 4ms before the I2C lines latch to the address */ + if (priv->gpiod_pwdn) + usleep_range(4000, 5000); + + /* + * The MAX9286 starts by default with all ports enabled, we disable all + * ports early to ensure that all channels are disabled if we error out + * and keep the bus consistent. + */ + max9286_i2c_mux_close(priv); + + /* + * The MAX9286 initialises with auto-acknowledge enabled by default. + * This can be invasive to other transactions on the same bus, so + * disable it early. It will be enabled only as and when needed. + */ + max9286_configure_i2c(priv, false); + + ret = max9286_register_gpio(priv); + if (ret) + goto err_powerdown; + + priv->regulator = devm_regulator_get(&client->dev, "poc"); + if (IS_ERR(priv->regulator)) { + if (PTR_ERR(priv->regulator) != -EPROBE_DEFER) + dev_err(&client->dev, + "Unable to get PoC regulator (%ld)\n", + PTR_ERR(priv->regulator)); + ret = PTR_ERR(priv->regulator); + goto err_powerdown; + } + + ret = max9286_parse_dt(priv); + if (ret) + goto err_powerdown; + + ret = max9286_init(&client->dev); + if (ret < 0) + goto err_cleanup_dt; + + return 0; + +err_cleanup_dt: + max9286_cleanup_dt(priv); +err_powerdown: + gpiod_set_value_cansleep(priv->gpiod_pwdn, 0); + + return ret; +} + +static int max9286_remove(struct i2c_client *client) +{ + struct max9286_priv *priv = i2c_get_clientdata(client); + + i2c_mux_del_adapters(priv->mux); + + max9286_v4l2_unregister(priv); + + regulator_disable(priv->regulator); + + gpiod_set_value_cansleep(priv->gpiod_pwdn, 0); + + max9286_cleanup_dt(priv); + + return 0; +} + +static const struct of_device_id max9286_dt_ids[] = { + { .compatible = "maxim,max9286" }, + {}, +}; +MODULE_DEVICE_TABLE(of, max9286_dt_ids); + +static struct i2c_driver max9286_i2c_driver = { + .driver = { + .name = "max9286", + .of_match_table = of_match_ptr(max9286_dt_ids), + }, + .probe_new = max9286_probe, + .remove = max9286_remove, +}; + +module_i2c_driver(max9286_i2c_driver); + +MODULE_DESCRIPTION("Maxim MAX9286 GMSL Deserializer Driver"); +MODULE_AUTHOR("Jacopo Mondi, Kieran Bingham, Laurent Pinchart, Niklas Söderlund, Vladimir Barinov"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 34009bffc1c6e6cf7f6a2a85e8e184c6e06193fc Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Fri, 12 Jun 2020 16:47:13 +0200 Subject: media: i2c: Add RDACM20 driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The RDACM20 is a GMSL camera supporting 1280x800 resolution images developed by IMI based on an Omnivision 10635 sensor and a Maxim MAX9271 GMSL serializer. The GMSL link carries power, control (I2C) and video data over a single coax cable. Signed-off-by: Jacopo Mondi Signed-off-by: Laurent Pinchart Signed-off-by: Niklas Söderlund Signed-off-by: Kieran Bingham Reviewed-by: Rob Herring Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 12 + drivers/media/i2c/Kconfig | 13 + drivers/media/i2c/Makefile | 2 + drivers/media/i2c/max9271.c | 341 ++++++++++++++++++++++ drivers/media/i2c/max9271.h | 224 +++++++++++++++ drivers/media/i2c/rdacm20.c | 667 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 1259 insertions(+) create mode 100644 drivers/media/i2c/max9271.c create mode 100644 drivers/media/i2c/max9271.h create mode 100644 drivers/media/i2c/rdacm20.c (limited to 'drivers/media') diff --git a/MAINTAINERS b/MAINTAINERS index 3bcedb12ab92..51c4cca11e0b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14412,6 +14412,18 @@ S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git dev F: tools/testing/selftests/rcutorture +RDACM20 Camera Sensor +M: Jacopo Mondi +M: Kieran Bingham +M: Laurent Pinchart +M: Niklas Söderlund +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml +F: drivers/media/i2c/rdacm20.c +F: drivers/media/i2c/max9271.c +F: drivers/media/i2c/max9271.h + RDC R-321X SoC M: Florian Fainelli S: Maintained diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 8471a6c438d4..48ae60a2c603 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1171,6 +1171,19 @@ config VIDEO_NOON010PC30 source "drivers/media/i2c/m5mols/Kconfig" +config VIDEO_RDACM20 + tristate "IMI RDACM20 camera support" + depends on I2C + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + select MEDIA_CONTROLLER + help + This driver supports the IMI RDACM20 GMSL camera, used in + ADAS systems. + + This camera should be used in conjunction with a GMSL + deserialiser such as the MAX9286. + config VIDEO_RJ54N1 tristate "Sharp RJ54N1CB0C sensor support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 9091f4918d4b..f0a77473979d 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -120,6 +120,8 @@ obj-$(CONFIG_VIDEO_IMX290) += imx290.o obj-$(CONFIG_VIDEO_IMX319) += imx319.o obj-$(CONFIG_VIDEO_IMX355) += imx355.o obj-$(CONFIG_VIDEO_MAX9286) += max9286.o +rdacm20-camera_module-objs := rdacm20.o max9271.o +obj-$(CONFIG_VIDEO_RDACM20) += rdacm20-camera_module.o obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o obj-$(CONFIG_SDR_MAX2175) += max2175.o diff --git a/drivers/media/i2c/max9271.c b/drivers/media/i2c/max9271.c new file mode 100644 index 000000000000..0f6f7a092a46 --- /dev/null +++ b/drivers/media/i2c/max9271.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017-2020 Jacopo Mondi + * Copyright (C) 2017-2020 Kieran Bingham + * Copyright (C) 2017-2020 Laurent Pinchart + * Copyright (C) 2017-2020 Niklas Söderlund + * Copyright (C) 2016 Renesas Electronics Corporation + * Copyright (C) 2015 Cogent Embedded, Inc. + * + * This file exports functions to control the Maxim MAX9271 GMSL serializer + * chip. This is not a self-contained driver, as MAX9271 is usually embedded in + * camera modules with at least one image sensor and optional additional + * components, such as uController units or ISPs/DSPs. + * + * Drivers for the camera modules (i.e. rdacm20/21) are expected to use + * functions exported from this library driver to maximize code re-use. + */ + +#include +#include + +#include "max9271.h" + +static int max9271_read(struct max9271_device *dev, u8 reg) +{ + int ret; + + dev_dbg(&dev->client->dev, "%s(0x%02x)\n", __func__, reg); + + ret = i2c_smbus_read_byte_data(dev->client, reg); + if (ret < 0) + dev_dbg(&dev->client->dev, + "%s: register 0x%02x read failed (%d)\n", + __func__, reg, ret); + + return ret; +} + +static int max9271_write(struct max9271_device *dev, u8 reg, u8 val) +{ + int ret; + + dev_dbg(&dev->client->dev, "%s(0x%02x, 0x%02x)\n", __func__, reg, val); + + ret = i2c_smbus_write_byte_data(dev->client, reg, val); + if (ret < 0) + dev_err(&dev->client->dev, + "%s: register 0x%02x write failed (%d)\n", + __func__, reg, ret); + + return ret; +} + +/* + * max9271_pclk_detect() - Detect valid pixel clock from image sensor + * + * Wait up to 10ms for a valid pixel clock. + * + * Returns 0 for success, < 0 for pixel clock not properly detected + */ +static int max9271_pclk_detect(struct max9271_device *dev) +{ + unsigned int i; + int ret; + + for (i = 0; i < 100; i++) { + ret = max9271_read(dev, 0x15); + if (ret < 0) + return ret; + + if (ret & MAX9271_PCLKDET) + return 0; + + usleep_range(50, 100); + } + + dev_err(&dev->client->dev, "Unable to detect valid pixel clock\n"); + + return -EIO; +} + +int max9271_set_serial_link(struct max9271_device *dev, bool enable) +{ + int ret; + u8 val = MAX9271_REVCCEN | MAX9271_FWDCCEN; + + if (enable) { + ret = max9271_pclk_detect(dev); + if (ret) + return ret; + + val |= MAX9271_SEREN; + } else { + val |= MAX9271_CLINKEN; + } + + /* + * The serializer temporarily disables the reverse control channel for + * 350µs after starting/stopping the forward serial link, but the + * deserializer synchronization time isn't clearly documented. + * + * According to the serializer datasheet we should wait 3ms, while + * according to the deserializer datasheet we should wait 5ms. + * + * Short delays here appear to show bit-errors in the writes following. + * Therefore a conservative delay seems best here. + */ + max9271_write(dev, 0x04, val); + usleep_range(5000, 8000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_set_serial_link); + +int max9271_configure_i2c(struct max9271_device *dev, u8 i2c_config) +{ + int ret; + + ret = max9271_write(dev, 0x0d, i2c_config); + if (ret) + return ret; + + /* The delay required after an I2C bus configuration change is not + * characterized in the serializer manual. Sleep up to 5msec to + * stay safe. + */ + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_configure_i2c); + +int max9271_set_high_threshold(struct max9271_device *dev, bool enable) +{ + int ret; + + ret = max9271_read(dev, 0x08); + if (ret < 0) + return ret; + + /* + * Enable or disable reverse channel high threshold to increase + * immunity to power supply noise. + */ + max9271_write(dev, 0x08, enable ? ret | BIT(0) : ret & ~BIT(0)); + usleep_range(2000, 2500); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_set_high_threshold); + +int max9271_configure_gmsl_link(struct max9271_device *dev) +{ + /* + * Configure the GMSL link: + * + * - Double input mode, high data rate, 24-bit mode + * - Latch input data on PCLKIN rising edge + * - Enable HS/VS encoding + * - 1-bit parity error detection + * + * TODO: Make the GMSL link configuration parametric. + */ + max9271_write(dev, 0x07, MAX9271_DBL | MAX9271_HVEN | + MAX9271_EDC_1BIT_PARITY); + usleep_range(5000, 8000); + + /* + * Adjust spread spectrum to +4% and auto-detect pixel clock + * and serial link rate. + */ + max9271_write(dev, 0x02, MAX9271_SPREAD_SPECT_4 | MAX9271_R02_RES | + MAX9271_PCLK_AUTODETECT | MAX9271_SERIAL_AUTODETECT); + usleep_range(5000, 8000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_configure_gmsl_link); + +int max9271_set_gpios(struct max9271_device *dev, u8 gpio_mask) +{ + int ret; + + ret = max9271_read(dev, 0x0f); + if (ret < 0) + return 0; + + ret |= gpio_mask; + ret = max9271_write(dev, 0x0f, ret); + if (ret < 0) { + dev_err(&dev->client->dev, "Failed to set gpio (%d)\n", ret); + return ret; + } + + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_set_gpios); + +int max9271_clear_gpios(struct max9271_device *dev, u8 gpio_mask) +{ + int ret; + + ret = max9271_read(dev, 0x0f); + if (ret < 0) + return 0; + + ret &= ~gpio_mask; + ret = max9271_write(dev, 0x0f, ret); + if (ret < 0) { + dev_err(&dev->client->dev, "Failed to clear gpio (%d)\n", ret); + return ret; + } + + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_clear_gpios); + +int max9271_enable_gpios(struct max9271_device *dev, u8 gpio_mask) +{ + int ret; + + ret = max9271_read(dev, 0x0f); + if (ret < 0) + return 0; + + /* BIT(0) reserved: GPO is always enabled. */ + ret |= gpio_mask | BIT(0); + ret = max9271_write(dev, 0x0e, ret); + if (ret < 0) { + dev_err(&dev->client->dev, "Failed to enable gpio (%d)\n", ret); + return ret; + } + + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_enable_gpios); + +int max9271_disable_gpios(struct max9271_device *dev, u8 gpio_mask) +{ + int ret; + + ret = max9271_read(dev, 0x0f); + if (ret < 0) + return 0; + + /* BIT(0) reserved: GPO cannot be disabled */ + ret &= (~gpio_mask | BIT(0)); + ret = max9271_write(dev, 0x0e, ret); + if (ret < 0) { + dev_err(&dev->client->dev, "Failed to disable gpio (%d)\n", ret); + return ret; + } + + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_disable_gpios); + +int max9271_verify_id(struct max9271_device *dev) +{ + int ret; + + ret = max9271_read(dev, 0x1e); + if (ret < 0) { + dev_err(&dev->client->dev, "MAX9271 ID read failed (%d)\n", + ret); + return ret; + } + + if (ret != MAX9271_ID) { + dev_err(&dev->client->dev, "MAX9271 ID mismatch (0x%02x)\n", + ret); + return -ENXIO; + } + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_verify_id); + +int max9271_set_address(struct max9271_device *dev, u8 addr) +{ + int ret; + + ret = max9271_write(dev, 0x00, addr << 1); + if (ret < 0) { + dev_err(&dev->client->dev, + "MAX9271 I2C address change failed (%d)\n", ret); + return ret; + } + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_set_address); + +int max9271_set_deserializer_address(struct max9271_device *dev, u8 addr) +{ + int ret; + + ret = max9271_write(dev, 0x01, addr << 1); + if (ret < 0) { + dev_err(&dev->client->dev, + "MAX9271 deserializer address set failed (%d)\n", ret); + return ret; + } + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_set_deserializer_address); + +int max9271_set_translation(struct max9271_device *dev, u8 source, u8 dest) +{ + int ret; + + ret = max9271_write(dev, 0x09, source << 1); + if (ret < 0) { + dev_err(&dev->client->dev, + "MAX9271 I2C translation setup failed (%d)\n", ret); + return ret; + } + usleep_range(3500, 5000); + + ret = max9271_write(dev, 0x0a, dest << 1); + if (ret < 0) { + dev_err(&dev->client->dev, + "MAX9271 I2C translation setup failed (%d)\n", ret); + return ret; + } + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_set_translation); diff --git a/drivers/media/i2c/max9271.h b/drivers/media/i2c/max9271.h new file mode 100644 index 000000000000..d78fb21441e9 --- /dev/null +++ b/drivers/media/i2c/max9271.h @@ -0,0 +1,224 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2017-2020 Jacopo Mondi + * Copyright (C) 2017-2020 Kieran Bingham + * Copyright (C) 2017-2020 Laurent Pinchart + * Copyright (C) 2017-2020 Niklas Söderlund + * Copyright (C) 2016 Renesas Electronics Corporation + * Copyright (C) 2015 Cogent Embedded, Inc. + */ + +#include + +#define MAX9271_DEFAULT_ADDR 0x40 + +/* Register 0x02 */ +#define MAX9271_SPREAD_SPECT_0 (0 << 5) +#define MAX9271_SPREAD_SPECT_05 (1 << 5) +#define MAX9271_SPREAD_SPECT_15 (2 << 5) +#define MAX9271_SPREAD_SPECT_1 (5 << 5) +#define MAX9271_SPREAD_SPECT_2 (3 << 5) +#define MAX9271_SPREAD_SPECT_3 (6 << 5) +#define MAX9271_SPREAD_SPECT_4 (7 << 5) +#define MAX9271_R02_RES BIT(4) +#define MAX9271_PCLK_AUTODETECT (3 << 2) +#define MAX9271_SERIAL_AUTODETECT (0x03) +/* Register 0x04 */ +#define MAX9271_SEREN BIT(7) +#define MAX9271_CLINKEN BIT(6) +#define MAX9271_PRBSEN BIT(5) +#define MAX9271_SLEEP BIT(4) +#define MAX9271_INTTYPE_I2C (0 << 2) +#define MAX9271_INTTYPE_UART (1 << 2) +#define MAX9271_INTTYPE_NONE (2 << 2) +#define MAX9271_REVCCEN BIT(1) +#define MAX9271_FWDCCEN BIT(0) +/* Register 0x07 */ +#define MAX9271_DBL BIT(7) +#define MAX9271_DRS BIT(6) +#define MAX9271_BWS BIT(5) +#define MAX9271_ES BIT(4) +#define MAX9271_HVEN BIT(2) +#define MAX9271_EDC_1BIT_PARITY (0 << 0) +#define MAX9271_EDC_6BIT_CRC (1 << 0) +#define MAX9271_EDC_6BIT_HAMMING (2 << 0) +/* Register 0x08 */ +#define MAX9271_INVVS BIT(7) +#define MAX9271_INVHS BIT(6) +#define MAX9271_REV_LOGAIN BIT(3) +#define MAX9271_REV_HIVTH BIT(0) +/* Register 0x09 */ +#define MAX9271_ID 0x09 +/* Register 0x0d */ +#define MAX9271_I2CLOCACK BIT(7) +#define MAX9271_I2CSLVSH_1046NS_469NS (3 << 5) +#define MAX9271_I2CSLVSH_938NS_352NS (2 << 5) +#define MAX9271_I2CSLVSH_469NS_234NS (1 << 5) +#define MAX9271_I2CSLVSH_352NS_117NS (0 << 5) +#define MAX9271_I2CMSTBT_837KBPS (7 << 2) +#define MAX9271_I2CMSTBT_533KBPS (6 << 2) +#define MAX9271_I2CMSTBT_339KBPS (5 << 2) +#define MAX9271_I2CMSTBT_173KBPS (4 << 2) +#define MAX9271_I2CMSTBT_105KBPS (3 << 2) +#define MAX9271_I2CMSTBT_84KBPS (2 << 2) +#define MAX9271_I2CMSTBT_28KBPS (1 << 2) +#define MAX9271_I2CMSTBT_8KBPS (0 << 2) +#define MAX9271_I2CSLVTO_NONE (3 << 0) +#define MAX9271_I2CSLVTO_1024US (2 << 0) +#define MAX9271_I2CSLVTO_256US (1 << 0) +#define MAX9271_I2CSLVTO_64US (0 << 0) +/* Register 0x0f */ +#define MAX9271_GPIO5OUT BIT(5) +#define MAX9271_GPIO4OUT BIT(4) +#define MAX9271_GPIO3OUT BIT(3) +#define MAX9271_GPIO2OUT BIT(2) +#define MAX9271_GPIO1OUT BIT(1) +#define MAX9271_GPO BIT(0) +/* Register 0x15 */ +#define MAX9271_PCLKDET BIT(0) + +/** + * struct max9271_device - max9271 device + * @client: The i2c client for the max9271 instance + */ +struct max9271_device { + struct i2c_client *client; +}; + +/** + * max9271_set_serial_link() - Enable/disable serial link + * @dev: The max9271 device + * @enable: Serial link enable/disable flag + * + * Return 0 on success or a negative error code on failure + */ +int max9271_set_serial_link(struct max9271_device *dev, bool enable); + +/** + * max9271_configure_i2c() - Configure I2C bus parameters + * @dev: The max9271 device + * @i2c_config: The I2C bus configuration bit mask + * + * Configure MAX9271 I2C interface. The bus configuration provided in the + * @i2c_config parameter shall be assembled using bit values defined by the + * MAX9271_I2C* macros. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_configure_i2c(struct max9271_device *dev, u8 i2c_config); + +/** + * max9271_set_high_threshold() - Enable or disable reverse channel high + * threshold + * @dev: The max9271 device + * @enable: High threshold enable/disable flag + * + * Return 0 on success or a negative error code on failure + */ +int max9271_set_high_threshold(struct max9271_device *dev, bool enable); + +/** + * max9271_configure_gmsl_link() - Configure the GMSL link + * @dev: The max9271 device + * + * FIXME: the GMSL link configuration is currently hardcoded and performed + * by programming registers 0x04, 0x07 and 0x02. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_configure_gmsl_link(struct max9271_device *dev); + +/** + * max9271_set_gpios() - Set gpio lines to physical high value + * @dev: The max9271 device + * @gpio_mask: The mask of gpio lines to set to high value + * + * The @gpio_mask parameter shall be assembled using the MAX9271_GP[IO|O]* + * bit values. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_set_gpios(struct max9271_device *dev, u8 gpio_mask); + +/** + * max9271_clear_gpios() - Set gpio lines to physical low value + * @dev: The max9271 device + * @gpio_mask: The mask of gpio lines to set to low value + * + * The @gpio_mask parameter shall be assembled using the MAX9271_GP[IO|O]* + * bit values. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_clear_gpios(struct max9271_device *dev, u8 gpio_mask); + +/** + * max9271_enable_gpios() - Enable gpio lines + * @dev: The max9271 device + * @gpio_mask: The mask of gpio lines to enable + * + * The @gpio_mask parameter shall be assembled using the MAX9271_GPIO* + * bit values. GPO line is always enabled by default. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_enable_gpios(struct max9271_device *dev, u8 gpio_mask); + +/** + * max9271_disable_gpios() - Disable gpio lines + * @dev: The max9271 device + * @gpio_mask: The mask of gpio lines to disable + * + * The @gpio_mask parameter shall be assembled using the MAX9271_GPIO* + * bit values. GPO line is always enabled by default and cannot be disabled. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_disable_gpios(struct max9271_device *dev, u8 gpio_mask); + +/** + * max9271_verify_id() - Read and verify MAX9271 id + * @dev: The max9271 device + * + * Return 0 on success or a negative error code on failure + */ +int max9271_verify_id(struct max9271_device *dev); + +/** + * max9271_set_address() - Program a new I2C address + * @dev: The max9271 device + * @addr: The new I2C address in 7-bit format + * + * This function only takes care of programming the new I2C address @addr to + * in the MAX9271 chip registers, it is responsiblity of the caller to set + * the i2c address client to the @addr value to be able to communicate with + * the MAX9271 chip using the I2C framework APIs after this function returns. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_set_address(struct max9271_device *dev, u8 addr); + +/** + * max9271_set_deserializer_address() - Program the remote deserializer address + * @dev: The max9271 device + * @addr: The deserializer I2C address in 7-bit format + * + * Return 0 on success or a negative error code on failure + */ +int max9271_set_deserializer_address(struct max9271_device *dev, u8 addr); + +/** + * max9271_set_translation() - Program I2C address translation + * @dev: The max9271 device + * @source: The I2C source address + * @dest: The I2C destination address + * + * Program address translation from @source to @dest. This is required to + * communicate with local devices that do not support address reprogramming. + * + * TODO: The device supports translation of two address, this function currently + * supports a single one. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_set_translation(struct max9271_device *dev, u8 source, u8 dest); diff --git a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c new file mode 100644 index 000000000000..1ed928c4ca70 --- /dev/null +++ b/drivers/media/i2c/rdacm20.c @@ -0,0 +1,667 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * IMI RDACM20 GMSL Camera Driver + * + * Copyright (C) 2017-2020 Jacopo Mondi + * Copyright (C) 2017-2020 Kieran Bingham + * Copyright (C) 2017-2019 Laurent Pinchart + * Copyright (C) 2017-2019 Niklas Söderlund + * Copyright (C) 2016 Renesas Electronics Corporation + * Copyright (C) 2015 Cogent Embedded, Inc. + */ + +/* + * The camera is made of an Omnivision OV10635 sensor connected to a Maxim + * MAX9271 GMSL serializer. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "max9271.h" + +#define OV10635_I2C_ADDRESS 0x30 + +#define OV10635_SOFTWARE_RESET 0x0103 +#define OV10635_PID 0x300a +#define OV10635_VER 0x300b +#define OV10635_SC_CMMN_SCCB_ID 0x300c +#define OV10635_SC_CMMN_SCCB_ID_SELECT BIT(0) +#define OV10635_VERSION 0xa635 + +#define OV10635_WIDTH 1280 +#define OV10635_HEIGHT 800 + +/* VTS = PCLK / FPS / HTS / 2 (= 88MHz / 1572 / 30 / 2) */ +#define OV10635_HTS 1572 +/* FPS = 29,9998 */ +#define OV10635_VTS 933 + +/* + * As the drivers supports a single MEDIA_BUS_FMT_UYVY8_2X8 format we + * can harcode the pixel rate. + * + * PCLK is fed through the system clock, programmed @88MHz. + * MEDIA_BUS_FMT_UYVY8_2X8 format = 2 samples per pixel. + * + * Pixelrate = PCLK / 2 + * FPS = (OV10635_VTS * OV10635_HTS) / PixelRate + * = 29,9998 + */ +#define OV10635_PIXEL_RATE (44000000) + +static const struct ov10635_reg { + u16 reg; + u8 val; +} ov10635_regs_wizard[] = { + { 0x301b, 0xff }, { 0x301c, 0xff }, { 0x301a, 0xff }, { 0x3011, 0x42 }, + { 0x6900, 0x0c }, { 0x6901, 0x19 }, { 0x3503, 0x10 }, { 0x3025, 0x03 }, + { 0x3003, 0x16 }, { 0x3004, 0x30 }, { 0x3005, 0x40 }, { 0x3006, 0x91 }, + { 0x3600, 0x74 }, { 0x3601, 0x2b }, { 0x3612, 0x00 }, { 0x3611, 0x67 }, + { 0x3633, 0xca }, { 0x3602, 0xaf }, { 0x3603, 0x04 }, { 0x3630, 0x28 }, + { 0x3631, 0x16 }, { 0x3714, 0x10 }, { 0x371d, 0x01 }, { 0x4300, 0x3a }, + { 0x3007, 0x01 }, { 0x3024, 0x03 }, { 0x3020, 0x0a }, { 0x3702, 0x0d }, + { 0x3703, 0x20 }, { 0x3704, 0x15 }, { 0x3709, 0xa8 }, { 0x370c, 0xc7 }, + { 0x370d, 0x80 }, { 0x3712, 0x00 }, { 0x3713, 0x20 }, { 0x3715, 0x04 }, + { 0x381d, 0x40 }, { 0x381c, 0x00 }, { 0x3822, 0x50 }, { 0x3824, 0x10 }, + { 0x3815, 0x8c }, { 0x3804, 0x05 }, { 0x3805, 0x1f }, { 0x3800, 0x00 }, + { 0x3801, 0x00 }, { 0x3806, 0x03 }, { 0x3807, 0x28 }, { 0x3802, 0x00 }, + { 0x3803, 0x07 }, { 0x3808, 0x05 }, { 0x3809, 0x00 }, { 0x380a, 0x03 }, + { 0x380b, 0x20 }, { 0x380c, OV10635_HTS >> 8 }, + { 0x380d, OV10635_HTS & 0xff }, { 0x380e, OV10635_VTS >> 8 }, + { 0x380f, OV10635_VTS & 0xff }, { 0x3813, 0x02 }, { 0x3811, 0x08 }, + { 0x381f, 0x0c }, { 0x3819, 0x04 }, { 0x3804, 0x01 }, { 0x3805, 0x00 }, + { 0x3828, 0x03 }, { 0x3829, 0x10 }, { 0x382a, 0x10 }, { 0x3621, 0x63 }, + { 0x5005, 0x08 }, { 0x56d5, 0x00 }, { 0x56d6, 0x80 }, { 0x56d7, 0x00 }, + { 0x56d8, 0x00 }, { 0x56d9, 0x00 }, { 0x56da, 0x80 }, { 0x56db, 0x00 }, + { 0x56dc, 0x00 }, { 0x56e8, 0x00 }, { 0x56e9, 0x7f }, { 0x56ea, 0x00 }, + { 0x56eb, 0x7f }, { 0x5100, 0x00 }, { 0x5101, 0x80 }, { 0x5102, 0x00 }, + { 0x5103, 0x80 }, { 0x5104, 0x00 }, { 0x5105, 0x80 }, { 0x5106, 0x00 }, + { 0x5107, 0x80 }, { 0x5108, 0x00 }, { 0x5109, 0x00 }, { 0x510a, 0x00 }, + { 0x510b, 0x00 }, { 0x510c, 0x00 }, { 0x510d, 0x00 }, { 0x510e, 0x00 }, + { 0x510f, 0x00 }, { 0x5110, 0x00 }, { 0x5111, 0x80 }, { 0x5112, 0x00 }, + { 0x5113, 0x80 }, { 0x5114, 0x00 }, { 0x5115, 0x80 }, { 0x5116, 0x00 }, + { 0x5117, 0x80 }, { 0x5118, 0x00 }, { 0x5119, 0x00 }, { 0x511a, 0x00 }, + { 0x511b, 0x00 }, { 0x511c, 0x00 }, { 0x511d, 0x00 }, { 0x511e, 0x00 }, + { 0x511f, 0x00 }, { 0x56d0, 0x00 }, { 0x5006, 0x04 }, { 0x5608, 0x05 }, + { 0x52d7, 0x06 }, { 0x528d, 0x08 }, { 0x5293, 0x12 }, { 0x52d3, 0x12 }, + { 0x5288, 0x06 }, { 0x5289, 0x20 }, { 0x52c8, 0x06 }, { 0x52c9, 0x20 }, + { 0x52cd, 0x04 }, { 0x5381, 0x00 }, { 0x5382, 0xff }, { 0x5589, 0x76 }, + { 0x558a, 0x47 }, { 0x558b, 0xef }, { 0x558c, 0xc9 }, { 0x558d, 0x49 }, + { 0x558e, 0x30 }, { 0x558f, 0x67 }, { 0x5590, 0x3f }, { 0x5591, 0xf0 }, + { 0x5592, 0x10 }, { 0x55a2, 0x6d }, { 0x55a3, 0x55 }, { 0x55a4, 0xc3 }, + { 0x55a5, 0xb5 }, { 0x55a6, 0x43 }, { 0x55a7, 0x38 }, { 0x55a8, 0x5f }, + { 0x55a9, 0x4b }, { 0x55aa, 0xf0 }, { 0x55ab, 0x10 }, { 0x5581, 0x52 }, + { 0x5300, 0x01 }, { 0x5301, 0x00 }, { 0x5302, 0x00 }, { 0x5303, 0x0e }, + { 0x5304, 0x00 }, { 0x5305, 0x0e }, { 0x5306, 0x00 }, { 0x5307, 0x36 }, + { 0x5308, 0x00 }, { 0x5309, 0xd9 }, { 0x530a, 0x00 }, { 0x530b, 0x0f }, + { 0x530c, 0x00 }, { 0x530d, 0x2c }, { 0x530e, 0x00 }, { 0x530f, 0x59 }, + { 0x5310, 0x00 }, { 0x5311, 0x7b }, { 0x5312, 0x00 }, { 0x5313, 0x22 }, + { 0x5314, 0x00 }, { 0x5315, 0xd5 }, { 0x5316, 0x00 }, { 0x5317, 0x13 }, + { 0x5318, 0x00 }, { 0x5319, 0x18 }, { 0x531a, 0x00 }, { 0x531b, 0x26 }, + { 0x531c, 0x00 }, { 0x531d, 0xdc }, { 0x531e, 0x00 }, { 0x531f, 0x02 }, + { 0x5320, 0x00 }, { 0x5321, 0x24 }, { 0x5322, 0x00 }, { 0x5323, 0x56 }, + { 0x5324, 0x00 }, { 0x5325, 0x85 }, { 0x5326, 0x00 }, { 0x5327, 0x20 }, + { 0x5609, 0x01 }, { 0x560a, 0x40 }, { 0x560b, 0x01 }, { 0x560c, 0x40 }, + { 0x560d, 0x00 }, { 0x560e, 0xfa }, { 0x560f, 0x00 }, { 0x5610, 0xfa }, + { 0x5611, 0x02 }, { 0x5612, 0x80 }, { 0x5613, 0x02 }, { 0x5614, 0x80 }, + { 0x5615, 0x01 }, { 0x5616, 0x2c }, { 0x5617, 0x01 }, { 0x5618, 0x2c }, + { 0x563b, 0x01 }, { 0x563c, 0x01 }, { 0x563d, 0x01 }, { 0x563e, 0x01 }, + { 0x563f, 0x03 }, { 0x5640, 0x03 }, { 0x5641, 0x03 }, { 0x5642, 0x05 }, + { 0x5643, 0x09 }, { 0x5644, 0x05 }, { 0x5645, 0x05 }, { 0x5646, 0x05 }, + { 0x5647, 0x05 }, { 0x5651, 0x00 }, { 0x5652, 0x80 }, { 0x521a, 0x01 }, + { 0x521b, 0x03 }, { 0x521c, 0x06 }, { 0x521d, 0x0a }, { 0x521e, 0x0e }, + { 0x521f, 0x12 }, { 0x5220, 0x16 }, { 0x5223, 0x02 }, { 0x5225, 0x04 }, + { 0x5227, 0x08 }, { 0x5229, 0x0c }, { 0x522b, 0x12 }, { 0x522d, 0x18 }, + { 0x522f, 0x1e }, { 0x5241, 0x04 }, { 0x5242, 0x01 }, { 0x5243, 0x03 }, + { 0x5244, 0x06 }, { 0x5245, 0x0a }, { 0x5246, 0x0e }, { 0x5247, 0x12 }, + { 0x5248, 0x16 }, { 0x524a, 0x03 }, { 0x524c, 0x04 }, { 0x524e, 0x08 }, + { 0x5250, 0x0c }, { 0x5252, 0x12 }, { 0x5254, 0x18 }, { 0x5256, 0x1e }, + /* fifo_line_length = 2*hts */ + { 0x4606, (2 * OV10635_HTS) >> 8 }, { 0x4607, (2 * OV10635_HTS) & 0xff }, + /* fifo_hsync_start = 2*(hts - xres) */ + { 0x460a, (2 * (OV10635_HTS - OV10635_WIDTH)) >> 8 }, + { 0x460b, (2 * (OV10635_HTS - OV10635_WIDTH)) & 0xff }, + { 0x460c, 0x00 }, { 0x4620, 0x0e }, + /* BT601: 0x08 is also acceptable as HS/VS mode */ + { 0x4700, 0x04 }, { 0x4701, 0x00 }, { 0x4702, 0x01 }, { 0x4004, 0x04 }, + { 0x4005, 0x18 }, { 0x4001, 0x06 }, { 0x4050, 0x22 }, { 0x4051, 0x24 }, + { 0x4052, 0x02 }, { 0x4057, 0x9c }, { 0x405a, 0x00 }, { 0x4202, 0x02 }, + { 0x3023, 0x10 }, { 0x0100, 0x01 }, { 0x0100, 0x01 }, { 0x6f10, 0x07 }, + { 0x6f11, 0x82 }, { 0x6f12, 0x04 }, { 0x6f13, 0x00 }, { 0xd000, 0x19 }, + { 0xd001, 0xa0 }, { 0xd002, 0x00 }, { 0xd003, 0x01 }, { 0xd004, 0xa9 }, + { 0xd005, 0xad }, { 0xd006, 0x10 }, { 0xd007, 0x40 }, { 0xd008, 0x44 }, + { 0xd009, 0x00 }, { 0xd00a, 0x68 }, { 0xd00b, 0x00 }, { 0xd00c, 0x15 }, + { 0xd00d, 0x00 }, { 0xd00e, 0x00 }, { 0xd00f, 0x00 }, { 0xd040, 0x9c }, + { 0xd041, 0x21 }, { 0xd042, 0xff }, { 0xd043, 0xf8 }, { 0xd044, 0xd4 }, + { 0xd045, 0x01 }, { 0xd046, 0x48 }, { 0xd047, 0x00 }, { 0xd048, 0xd4 }, + { 0xd049, 0x01 }, { 0xd04a, 0x50 }, { 0xd04b, 0x04 }, { 0xd04c, 0x18 }, + { 0xd04d, 0x60 }, { 0xd04e, 0x00 }, { 0xd04f, 0x01 }, { 0xd050, 0xa8 }, + { 0xd051, 0x63 }, { 0xd052, 0x02 }, { 0xd053, 0xa4 }, { 0xd054, 0x85 }, + { 0xd055, 0x43 }, { 0xd056, 0x00 }, { 0xd057, 0x00 }, { 0xd058, 0x18 }, + { 0xd059, 0x60 }, { 0xd05a, 0x00 }, { 0xd05b, 0x01 }, { 0xd05c, 0xa8 }, + { 0xd05d, 0x63 }, { 0xd05e, 0x03 }, { 0xd05f, 0xf0 }, { 0xd060, 0x98 }, + { 0xd061, 0xa3 }, { 0xd062, 0x00 }, { 0xd063, 0x00 }, { 0xd064, 0x8c }, + { 0xd065, 0x6a }, { 0xd066, 0x00 }, { 0xd067, 0x6e }, { 0xd068, 0xe5 }, + { 0xd069, 0x85 }, { 0xd06a, 0x18 }, { 0xd06b, 0x00 }, { 0xd06c, 0x10 }, + { 0xd06d, 0x00 }, { 0xd06e, 0x00 }, { 0xd06f, 0x10 }, { 0xd070, 0x9c }, + { 0xd071, 0x80 }, { 0xd072, 0x00 }, { 0xd073, 0x03 }, { 0xd074, 0x18 }, + { 0xd075, 0x60 }, { 0xd076, 0x00 }, { 0xd077, 0x01 }, { 0xd078, 0xa8 }, + { 0xd079, 0x63 }, { 0xd07a, 0x07 }, { 0xd07b, 0x80 }, { 0xd07c, 0x07 }, + { 0xd07d, 0xff }, { 0xd07e, 0xf9 }, { 0xd07f, 0x03 }, { 0xd080, 0x8c }, + { 0xd081, 0x63 }, { 0xd082, 0x00 }, { 0xd083, 0x00 }, { 0xd084, 0xa5 }, + { 0xd085, 0x6b }, { 0xd086, 0x00 }, { 0xd087, 0xff }, { 0xd088, 0x18 }, + { 0xd089, 0x80 }, { 0xd08a, 0x00 }, { 0xd08b, 0x01 }, { 0xd08c, 0xa8 }, + { 0xd08d, 0x84 }, { 0xd08e, 0x01 }, { 0xd08f, 0x04 }, { 0xd090, 0xe1 }, + { 0xd091, 0x6b }, { 0xd092, 0x58 }, { 0xd093, 0x00 }, { 0xd094, 0x94 }, + { 0xd095, 0x6a }, { 0xd096, 0x00 }, { 0xd097, 0x70 }, { 0xd098, 0xe1 }, + { 0xd099, 0x6b }, { 0xd09a, 0x20 }, { 0xd09b, 0x00 }, { 0xd09c, 0x95 }, + { 0xd09d, 0x6b }, { 0xd09e, 0x00 }, { 0xd09f, 0x00 }, { 0xd0a0, 0xe4 }, + { 0xd0a1, 0x8b }, { 0xd0a2, 0x18 }, { 0xd0a3, 0x00 }, { 0xd0a4, 0x0c }, + { 0xd0a5, 0x00 }, { 0xd0a6, 0x00 }, { 0xd0a7, 0x23 }, { 0xd0a8, 0x15 }, + { 0xd0a9, 0x00 }, { 0xd0aa, 0x00 }, { 0xd0ab, 0x00 }, { 0xd0ac, 0x18 }, + { 0xd0ad, 0x60 }, { 0xd0ae, 0x80 }, { 0xd0af, 0x06 }, { 0xd0b0, 0xa8 }, + { 0xd0b1, 0x83 }, { 0xd0b2, 0x40 }, { 0xd0b3, 0x08 }, { 0xd0b4, 0xa8 }, + { 0xd0b5, 0xe3 }, { 0xd0b6, 0x38 }, { 0xd0b7, 0x2a }, { 0xd0b8, 0xa8 }, + { 0xd0b9, 0xc3 }, { 0xd0ba, 0x40 }, { 0xd0bb, 0x09 }, { 0xd0bc, 0xa8 }, + { 0xd0bd, 0xa3 }, { 0xd0be, 0x38 }, { 0xd0bf, 0x29 }, { 0xd0c0, 0x8c }, + { 0xd0c1, 0x65 }, { 0xd0c2, 0x00 }, { 0xd0c3, 0x00 }, { 0xd0c4, 0xd8 }, + { 0xd0c5, 0x04 }, { 0xd0c6, 0x18 }, { 0xd0c7, 0x00 }, { 0xd0c8, 0x8c }, + { 0xd0c9, 0x67 }, { 0xd0ca, 0x00 }, { 0xd0cb, 0x00 }, { 0xd0cc, 0xd8 }, + { 0xd0cd, 0x06 }, { 0xd0ce, 0x18 }, { 0xd0cf, 0x00 }, { 0xd0d0, 0x18 }, + { 0xd0d1, 0x60 }, { 0xd0d2, 0x80 }, { 0xd0d3, 0x06 }, { 0xd0d4, 0xa8 }, + { 0xd0d5, 0xe3 }, { 0xd0d6, 0x67 }, { 0xd0d7, 0x02 }, { 0xd0d8, 0xa9 }, + { 0xd0d9, 0x03 }, { 0xd0da, 0x67 }, { 0xd0db, 0x03 }, { 0xd0dc, 0xa8 }, + { 0xd0dd, 0xc3 }, { 0xd0de, 0x3d }, { 0xd0df, 0x05 }, { 0xd0e0, 0x8c }, + { 0xd0e1, 0x66 }, { 0xd0e2, 0x00 }, { 0xd0e3, 0x00 }, { 0xd0e4, 0xb8 }, + { 0xd0e5, 0x63 }, { 0xd0e6, 0x00 }, { 0xd0e7, 0x18 }, { 0xd0e8, 0xb8 }, + { 0xd0e9, 0x63 }, { 0xd0ea, 0x00 }, { 0xd0eb, 0x98 }, { 0xd0ec, 0xbc }, + { 0xd0ed, 0x03 }, { 0xd0ee, 0x00 }, { 0xd0ef, 0x00 }, { 0xd0f0, 0x10 }, + { 0xd0f1, 0x00 }, { 0xd0f2, 0x00 }, { 0xd0f3, 0x16 }, { 0xd0f4, 0xb8 }, + { 0xd0f5, 0x83 }, { 0xd0f6, 0x00 }, { 0xd0f7, 0x19 }, { 0xd0f8, 0x8c }, + { 0xd0f9, 0x67 }, { 0xd0fa, 0x00 }, { 0xd0fb, 0x00 }, { 0xd0fc, 0xb8 }, + { 0xd0fd, 0xa4 }, { 0xd0fe, 0x00 }, { 0xd0ff, 0x98 }, { 0xd100, 0xb8 }, + { 0xd101, 0x83 }, { 0xd102, 0x00 }, { 0xd103, 0x08 }, { 0xd104, 0x8c }, + { 0xd105, 0x68 }, { 0xd106, 0x00 }, { 0xd107, 0x00 }, { 0xd108, 0xe0 }, + { 0xd109, 0x63 }, { 0xd10a, 0x20 }, { 0xd10b, 0x04 }, { 0xd10c, 0xe0 }, + { 0xd10d, 0x65 }, { 0xd10e, 0x18 }, { 0xd10f, 0x00 }, { 0xd110, 0xa4 }, + { 0xd111, 0x83 }, { 0xd112, 0xff }, { 0xd113, 0xff }, { 0xd114, 0xb8 }, + { 0xd115, 0x64 }, { 0xd116, 0x00 }, { 0xd117, 0x48 }, { 0xd118, 0xd8 }, + { 0xd119, 0x07 }, { 0xd11a, 0x18 }, { 0xd11b, 0x00 }, { 0xd11c, 0xd8 }, + { 0xd11d, 0x08 }, { 0xd11e, 0x20 }, { 0xd11f, 0x00 }, { 0xd120, 0x9c }, + { 0xd121, 0x60 }, { 0xd122, 0x00 }, { 0xd123, 0x00 }, { 0xd124, 0xd8 }, + { 0xd125, 0x06 }, { 0xd126, 0x18 }, { 0xd127, 0x00 }, { 0xd128, 0x00 }, + { 0xd129, 0x00 }, { 0xd12a, 0x00 }, { 0xd12b, 0x08 }, { 0xd12c, 0x15 }, + { 0xd12d, 0x00 }, { 0xd12e, 0x00 }, { 0xd12f, 0x00 }, { 0xd130, 0x8c }, + { 0xd131, 0x6a }, { 0xd132, 0x00 }, { 0xd133, 0x76 }, { 0xd134, 0xbc }, + { 0xd135, 0x23 }, { 0xd136, 0x00 }, { 0xd137, 0x00 }, { 0xd138, 0x13 }, + { 0xd139, 0xff }, { 0xd13a, 0xff }, { 0xd13b, 0xe6 }, { 0xd13c, 0x18 }, + { 0xd13d, 0x60 }, { 0xd13e, 0x80 }, { 0xd13f, 0x06 }, { 0xd140, 0x03 }, + { 0xd141, 0xff }, { 0xd142, 0xff }, { 0xd143, 0xdd }, { 0xd144, 0xa8 }, + { 0xd145, 0x83 }, { 0xd146, 0x40 }, { 0xd147, 0x08 }, { 0xd148, 0x85 }, + { 0xd149, 0x21 }, { 0xd14a, 0x00 }, { 0xd14b, 0x00 }, { 0xd14c, 0x85 }, + { 0xd14d, 0x41 }, { 0xd14e, 0x00 }, { 0xd14f, 0x04 }, { 0xd150, 0x44 }, + { 0xd151, 0x00 }, { 0xd152, 0x48 }, { 0xd153, 0x00 }, { 0xd154, 0x9c }, + { 0xd155, 0x21 }, { 0xd156, 0x00 }, { 0xd157, 0x08 }, { 0x6f0e, 0x03 }, + { 0x6f0f, 0x00 }, { 0x460e, 0x08 }, { 0x460f, 0x01 }, { 0x4610, 0x00 }, + { 0x4611, 0x01 }, { 0x4612, 0x00 }, { 0x4613, 0x01 }, + /* 8 bits */ + { 0x4605, 0x08 }, + /* Swap data bits order [9:0] -> [0:9] */ + { 0x4709, 0x10 }, { 0x4608, 0x00 }, { 0x4609, 0x08 }, { 0x6804, 0x00 }, + { 0x6805, 0x06 }, { 0x6806, 0x00 }, { 0x5120, 0x00 }, { 0x3510, 0x00 }, + { 0x3504, 0x00 }, { 0x6800, 0x00 }, { 0x6f0d, 0x01 }, + /* PCLK falling edge */ + { 0x4708, 0x01 }, { 0x5000, 0xff }, { 0x5001, 0xbf }, { 0x5002, 0x7e }, + { 0x503d, 0x00 }, { 0xc450, 0x01 }, { 0xc452, 0x04 }, { 0xc453, 0x00 }, + { 0xc454, 0x00 }, { 0xc455, 0x01 }, { 0xc456, 0x01 }, { 0xc457, 0x00 }, + { 0xc458, 0x00 }, { 0xc459, 0x00 }, { 0xc45b, 0x00 }, { 0xc45c, 0x01 }, + { 0xc45d, 0x00 }, { 0xc45e, 0x00 }, { 0xc45f, 0x00 }, { 0xc460, 0x00 }, + { 0xc461, 0x01 }, { 0xc462, 0x01 }, { 0xc464, 0x03 }, { 0xc465, 0x00 }, + { 0xc466, 0x8a }, { 0xc467, 0x00 }, { 0xc468, 0x86 }, { 0xc469, 0x00 }, + { 0xc46a, 0x40 }, { 0xc46b, 0x50 }, { 0xc46c, 0x30 }, { 0xc46d, 0x28 }, + { 0xc46e, 0x60 }, { 0xc46f, 0x40 }, { 0xc47c, 0x01 }, { 0xc47d, 0x38 }, + { 0xc47e, 0x00 }, { 0xc47f, 0x00 }, { 0xc480, 0x00 }, { 0xc481, 0xff }, + { 0xc482, 0x00 }, { 0xc483, 0x40 }, { 0xc484, 0x00 }, { 0xc485, 0x18 }, + { 0xc486, 0x00 }, { 0xc487, 0x18 }, + { 0xc488, (OV10635_VTS - 8) * 16 >> 8}, + { 0xc489, (OV10635_VTS - 8) * 16 & 0xff}, + { 0xc48a, (OV10635_VTS - 8) * 16 >> 8}, + { 0xc48b, (OV10635_VTS - 8) * 16 & 0xff}, { 0xc48c, 0x00 }, + { 0xc48d, 0x04 }, { 0xc48e, 0x00 }, { 0xc48f, 0x04 }, { 0xc490, 0x03 }, + { 0xc492, 0x20 }, { 0xc493, 0x08 }, { 0xc498, 0x02 }, { 0xc499, 0x00 }, + { 0xc49a, 0x02 }, { 0xc49b, 0x00 }, { 0xc49c, 0x02 }, { 0xc49d, 0x00 }, + { 0xc49e, 0x02 }, { 0xc49f, 0x60 }, { 0xc4a0, 0x03 }, { 0xc4a1, 0x00 }, + { 0xc4a2, 0x04 }, { 0xc4a3, 0x00 }, { 0xc4a4, 0x00 }, { 0xc4a5, 0x10 }, + { 0xc4a6, 0x00 }, { 0xc4a7, 0x40 }, { 0xc4a8, 0x00 }, { 0xc4a9, 0x80 }, + { 0xc4aa, 0x0d }, { 0xc4ab, 0x00 }, { 0xc4ac, 0x0f }, { 0xc4ad, 0xc0 }, + { 0xc4b4, 0x01 }, { 0xc4b5, 0x01 }, { 0xc4b6, 0x00 }, { 0xc4b7, 0x01 }, + { 0xc4b8, 0x00 }, { 0xc4b9, 0x01 }, { 0xc4ba, 0x01 }, { 0xc4bb, 0x00 }, + { 0xc4bc, 0x01 }, { 0xc4bd, 0x60 }, { 0xc4be, 0x02 }, { 0xc4bf, 0x33 }, + { 0xc4c8, 0x03 }, { 0xc4c9, 0xd0 }, { 0xc4ca, 0x0e }, { 0xc4cb, 0x00 }, + { 0xc4cc, 0x0e }, { 0xc4cd, 0x51 }, { 0xc4ce, 0x0e }, { 0xc4cf, 0x51 }, + { 0xc4d0, 0x04 }, { 0xc4d1, 0x80 }, { 0xc4e0, 0x04 }, { 0xc4e1, 0x02 }, + { 0xc4e2, 0x01 }, { 0xc4e4, 0x10 }, { 0xc4e5, 0x20 }, { 0xc4e6, 0x30 }, + { 0xc4e7, 0x40 }, { 0xc4e8, 0x50 }, { 0xc4e9, 0x60 }, { 0xc4ea, 0x70 }, + { 0xc4eb, 0x80 }, { 0xc4ec, 0x90 }, { 0xc4ed, 0xa0 }, { 0xc4ee, 0xb0 }, + { 0xc4ef, 0xc0 }, { 0xc4f0, 0xd0 }, { 0xc4f1, 0xe0 }, { 0xc4f2, 0xf0 }, + { 0xc4f3, 0x80 }, { 0xc4f4, 0x00 }, { 0xc4f5, 0x20 }, { 0xc4f6, 0x02 }, + { 0xc4f7, 0x00 }, { 0xc4f8, 0x00 }, { 0xc4f9, 0x00 }, { 0xc4fa, 0x00 }, + { 0xc4fb, 0x01 }, { 0xc4fc, 0x01 }, { 0xc4fd, 0x00 }, { 0xc4fe, 0x04 }, + { 0xc4ff, 0x02 }, { 0xc500, 0x48 }, { 0xc501, 0x74 }, { 0xc502, 0x58 }, + { 0xc503, 0x80 }, { 0xc504, 0x05 }, { 0xc505, 0x80 }, { 0xc506, 0x03 }, + { 0xc507, 0x80 }, { 0xc508, 0x01 }, { 0xc509, 0xc0 }, { 0xc50a, 0x01 }, + { 0xc50b, 0xa0 }, { 0xc50c, 0x01 }, { 0xc50d, 0x2c }, { 0xc50e, 0x01 }, + { 0xc50f, 0x0a }, { 0xc510, 0x00 }, { 0xc511, 0x00 }, { 0xc512, 0xe5 }, + { 0xc513, 0x14 }, { 0xc514, 0x04 }, { 0xc515, 0x00 }, { 0xc518, OV10635_VTS >> 8}, + { 0xc519, OV10635_VTS & 0xff}, { 0xc51a, OV10635_HTS >> 8}, + { 0xc51b, OV10635_HTS & 0xff}, { 0xc2e0, 0x00 }, { 0xc2e1, 0x51 }, + { 0xc2e2, 0x00 }, { 0xc2e3, 0xd6 }, { 0xc2e4, 0x01 }, { 0xc2e5, 0x5e }, + { 0xc2e9, 0x01 }, { 0xc2ea, 0x7a }, { 0xc2eb, 0x90 }, { 0xc2ed, 0x00 }, + { 0xc2ee, 0x7a }, { 0xc2ef, 0x64 }, { 0xc308, 0x00 }, { 0xc309, 0x00 }, + { 0xc30a, 0x00 }, { 0xc30c, 0x00 }, { 0xc30d, 0x01 }, { 0xc30e, 0x00 }, + { 0xc30f, 0x00 }, { 0xc310, 0x01 }, { 0xc311, 0x60 }, { 0xc312, 0xff }, + { 0xc313, 0x08 }, { 0xc314, 0x01 }, { 0xc315, 0x00 }, { 0xc316, 0xff }, + { 0xc317, 0x0b }, { 0xc318, 0x00 }, { 0xc319, 0x0c }, { 0xc31a, 0x00 }, + { 0xc31b, 0xe0 }, { 0xc31c, 0x00 }, { 0xc31d, 0x14 }, { 0xc31e, 0x00 }, + { 0xc31f, 0xc5 }, { 0xc320, 0xff }, { 0xc321, 0x4b }, { 0xc322, 0xff }, + { 0xc323, 0xf0 }, { 0xc324, 0xff }, { 0xc325, 0xe8 }, { 0xc326, 0x00 }, + { 0xc327, 0x46 }, { 0xc328, 0xff }, { 0xc329, 0xd2 }, { 0xc32a, 0xff }, + { 0xc32b, 0xe4 }, { 0xc32c, 0xff }, { 0xc32d, 0xbb }, { 0xc32e, 0x00 }, + { 0xc32f, 0x61 }, { 0xc330, 0xff }, { 0xc331, 0xf9 }, { 0xc332, 0x00 }, + { 0xc333, 0xd9 }, { 0xc334, 0x00 }, { 0xc335, 0x2e }, { 0xc336, 0x00 }, + { 0xc337, 0xb1 }, { 0xc338, 0xff }, { 0xc339, 0x64 }, { 0xc33a, 0xff }, + { 0xc33b, 0xeb }, { 0xc33c, 0xff }, { 0xc33d, 0xe8 }, { 0xc33e, 0x00 }, + { 0xc33f, 0x48 }, { 0xc340, 0xff }, { 0xc341, 0xd0 }, { 0xc342, 0xff }, + { 0xc343, 0xed }, { 0xc344, 0xff }, { 0xc345, 0xad }, { 0xc346, 0x00 }, + { 0xc347, 0x66 }, { 0xc348, 0x01 }, { 0xc349, 0x00 }, { 0x6700, 0x04 }, + { 0x6701, 0x7b }, { 0x6702, 0xfd }, { 0x6703, 0xf9 }, { 0x6704, 0x3d }, + { 0x6705, 0x71 }, { 0x6706, 0x78 }, { 0x6708, 0x05 }, { 0x6f06, 0x6f }, + { 0x6f07, 0x00 }, { 0x6f0a, 0x6f }, { 0x6f0b, 0x00 }, { 0x6f00, 0x03 }, + { 0xc34c, 0x01 }, { 0xc34d, 0x00 }, { 0xc34e, 0x46 }, { 0xc34f, 0x55 }, + { 0xc350, 0x00 }, { 0xc351, 0x40 }, { 0xc352, 0x00 }, { 0xc353, 0xff }, + { 0xc354, 0x04 }, { 0xc355, 0x08 }, { 0xc356, 0x01 }, { 0xc357, 0xef }, + { 0xc358, 0x30 }, { 0xc359, 0x01 }, { 0xc35a, 0x64 }, { 0xc35b, 0x46 }, + { 0xc35c, 0x00 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0xc261, 0x01 }, + { 0x301b, 0xf0 }, { 0x301c, 0xf0 }, { 0x301a, 0xf0 }, { 0x6f00, 0xc3 }, + { 0xc46a, 0x30 }, { 0xc46d, 0x20 }, { 0xc464, 0x84 }, { 0xc465, 0x00 }, + { 0x6f00, 0x03 }, { 0x6f00, 0x43 }, { 0x381c, 0x00 }, { 0x381d, 0x40 }, + { 0xc454, 0x01 }, { 0x6f00, 0xc3 }, { 0xc454, 0x00 }, { 0xc4b1, 0x02 }, + { 0xc4b2, 0x01 }, { 0xc4b3, 0x03 }, { 0x6f00, 0x03 }, { 0x6f00, 0x43 }, + /* enable FSIN (FRAMESYNC input) functionality */ + { 0x3832, (0x0d + 2 * 0x20 + 0x15 + 38) >> 8 }, + { 0x3833, (0x0d + 2 * 0x20 + 0x15 + 38) & 0xff }, + { 0x3834, OV10635_VTS >> 8 }, { 0x3835, OV10635_VTS & 0xff }, + { 0x302e, 0x01 }, +}; + +struct rdacm20_device { + struct device *dev; + struct max9271_device *serializer; + struct i2c_client *sensor; + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler ctrls; + u32 addrs[2]; +}; + +static inline struct rdacm20_device *sd_to_rdacm20(struct v4l2_subdev *sd) +{ + return container_of(sd, struct rdacm20_device, sd); +} + +static inline struct rdacm20_device *i2c_to_rdacm20(struct i2c_client *client) +{ + return sd_to_rdacm20(i2c_get_clientdata(client)); +} + +static int ov10635_read16(struct rdacm20_device *dev, u16 reg) +{ + u8 buf[2] = { reg >> 8, reg & 0xff }; + int ret; + + ret = i2c_master_send(dev->sensor, buf, 2); + if (ret != 2) { + dev_dbg(dev->dev, "%s: register 0x%04x write failed (%d)\n", + __func__, reg, ret); + return ret; + } + + ret = i2c_master_recv(dev->sensor, buf, 2); + if (ret < 0) { + dev_dbg(dev->dev, "%s: register 0x%04x read failed (%d)\n", + __func__, reg, ret); + return ret; + } + + return (buf[0] << 8) | buf[1]; +} + +static int __ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val) +{ + u8 buf[3] = { reg >> 8, reg & 0xff, val }; + int ret; + + dev_dbg(dev->dev, "%s(0x%04x, 0x%02x)\n", __func__, reg, val); + + ret = i2c_master_send(dev->sensor, buf, 3); + return ret < 0 ? ret : 0; +} + +static int ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val) +{ + int ret; + + ret = __ov10635_write(dev, reg, val); + if (ret < 0) + dev_err(dev->dev, "%s: register 0x%04x write failed (%d)\n", + __func__, reg, ret); + + return ret; +} + +static int ov10635_set_regs(struct rdacm20_device *dev, + const struct ov10635_reg *regs, + unsigned int nr_regs) +{ + unsigned int i; + int ret; + + for (i = 0; i < nr_regs; i++) { + ret = __ov10635_write(dev, regs[i].reg, regs[i].val); + if (ret) { + dev_err(dev->dev, + "%s: register %u (0x%04x) write failed (%d)\n", + __func__, i, regs[i].reg, ret); + return ret; + } + } + + return 0; +} + +static int rdacm20_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct rdacm20_device *dev = sd_to_rdacm20(sd); + + return max9271_set_serial_link(dev->serializer, enable); +} + +static int rdacm20_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_UYVY8_2X8; + + return 0; +} + +static int rdacm20_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + + if (format->pad) + return -EINVAL; + + mf->width = OV10635_WIDTH; + mf->height = OV10635_HEIGHT; + mf->code = MEDIA_BUS_FMT_UYVY8_2X8; + mf->colorspace = V4L2_COLORSPACE_RAW; + mf->field = V4L2_FIELD_NONE; + mf->ycbcr_enc = V4L2_YCBCR_ENC_601; + mf->quantization = V4L2_QUANTIZATION_FULL_RANGE; + mf->xfer_func = V4L2_XFER_FUNC_NONE; + + return 0; +} + +static struct v4l2_subdev_video_ops rdacm20_video_ops = { + .s_stream = rdacm20_s_stream, +}; + +static const struct v4l2_subdev_pad_ops rdacm20_subdev_pad_ops = { + .enum_mbus_code = rdacm20_enum_mbus_code, + .get_fmt = rdacm20_get_fmt, + .set_fmt = rdacm20_get_fmt, +}; + +static struct v4l2_subdev_ops rdacm20_subdev_ops = { + .video = &rdacm20_video_ops, + .pad = &rdacm20_subdev_pad_ops, +}; + +static int rdacm20_initialize(struct rdacm20_device *dev) +{ + unsigned int retry = 3; + int ret; + + /* Verify communication with the MAX9271: ping to wakeup. */ + dev->serializer->client->addr = MAX9271_DEFAULT_ADDR; + i2c_smbus_read_byte(dev->serializer->client); + + /* Serial link disabled during config as it needs a valid pixel clock. */ + ret = max9271_set_serial_link(dev->serializer, false); + if (ret) + return ret; + + /* + * Ensure that we have a good link configuration before attempting to + * identify the device. + */ + max9271_configure_i2c(dev->serializer, MAX9271_I2CSLVSH_469NS_234NS | + MAX9271_I2CSLVTO_1024US | + MAX9271_I2CMSTBT_105KBPS); + + max9271_configure_gmsl_link(dev->serializer); + + ret = max9271_verify_id(dev->serializer); + if (ret < 0) + return ret; + + ret = max9271_set_address(dev->serializer, dev->addrs[0]); + if (ret < 0) + return ret; + dev->serializer->client->addr = dev->addrs[0]; + + /* + * Reset the sensor by cycling the OV10635 reset signal connected to the + * MAX9271 GPIO1 and verify communication with the OV10635. + */ + max9271_clear_gpios(dev->serializer, MAX9271_GPIO1OUT); + usleep_range(10000, 15000); + max9271_set_gpios(dev->serializer, MAX9271_GPIO1OUT); + usleep_range(10000, 15000); + +again: + ret = ov10635_read16(dev, OV10635_PID); + if (ret < 0) { + if (retry--) + goto again; + + dev_err(dev->dev, "OV10635 ID read failed (%d)\n", + ret); + return -ENXIO; + } + + if (ret != OV10635_VERSION) { + if (retry--) + goto again; + + dev_err(dev->dev, "OV10635 ID mismatch (0x%04x)\n", + ret); + return -ENXIO; + } + + /* Change the sensor I2C address. */ + ret = ov10635_write(dev, OV10635_SC_CMMN_SCCB_ID, + (dev->addrs[1] << 1) | + OV10635_SC_CMMN_SCCB_ID_SELECT); + if (ret < 0) { + dev_err(dev->dev, + "OV10635 I2C address change failed (%d)\n", ret); + return ret; + } + dev->sensor->addr = dev->addrs[1]; + usleep_range(3500, 5000); + + /* Program the 0V10635 initial configuration. */ + ret = ov10635_set_regs(dev, ov10635_regs_wizard, + ARRAY_SIZE(ov10635_regs_wizard)); + if (ret) + return ret; + + dev_info(dev->dev, "Identified MAX9271 + OV10635 device\n"); + + return 0; +} + +static int rdacm20_probe(struct i2c_client *client) +{ + struct rdacm20_device *dev; + struct fwnode_handle *ep; + int ret; + + dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->dev = &client->dev; + + dev->serializer = devm_kzalloc(&client->dev, sizeof(*dev->serializer), + GFP_KERNEL); + if (!dev->serializer) + return -ENOMEM; + + dev->serializer->client = client; + + ret = of_property_read_u32_array(client->dev.of_node, "reg", + dev->addrs, 2); + if (ret < 0) { + dev_err(dev->dev, "Invalid DT reg property: %d\n", ret); + return -EINVAL; + } + + /* Create the dummy I2C client for the sensor. */ + dev->sensor = i2c_new_dummy_device(client->adapter, + OV10635_I2C_ADDRESS); + if (IS_ERR(dev->sensor)) { + ret = PTR_ERR(dev->sensor); + goto error; + } + + /* Initialize the hardware. */ + ret = rdacm20_initialize(dev); + if (ret < 0) + goto error; + + /* Initialize and register the subdevice. */ + v4l2_i2c_subdev_init(&dev->sd, client, &rdacm20_subdev_ops); + dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + v4l2_ctrl_handler_init(&dev->ctrls, 1); + v4l2_ctrl_new_std(&dev->ctrls, NULL, V4L2_CID_PIXEL_RATE, + OV10635_PIXEL_RATE, OV10635_PIXEL_RATE, 1, + OV10635_PIXEL_RATE); + dev->sd.ctrl_handler = &dev->ctrls; + + ret = dev->ctrls.error; + if (ret) + goto error_free_ctrls; + + dev->pad.flags = MEDIA_PAD_FL_SOURCE; + dev->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad); + if (ret < 0) + goto error_free_ctrls; + + ep = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); + if (!ep) { + dev_err(&client->dev, + "Unable to get endpoint in node %pOF\n", + client->dev.of_node); + ret = -ENOENT; + goto error_free_ctrls; + } + dev->sd.fwnode = ep; + + ret = v4l2_async_register_subdev(&dev->sd); + if (ret) + goto error_put_node; + + return 0; + +error_put_node: + fwnode_handle_put(ep); +error_free_ctrls: + v4l2_ctrl_handler_free(&dev->ctrls); +error: + media_entity_cleanup(&dev->sd.entity); + if (dev->sensor) + i2c_unregister_device(dev->sensor); + + dev_err(&client->dev, "probe failed\n"); + + return ret; +} + +static int rdacm20_remove(struct i2c_client *client) +{ + struct rdacm20_device *dev = i2c_to_rdacm20(client); + + fwnode_handle_put(dev->sd.fwnode); + v4l2_async_unregister_subdev(&dev->sd); + v4l2_ctrl_handler_free(&dev->ctrls); + media_entity_cleanup(&dev->sd.entity); + i2c_unregister_device(dev->sensor); + + return 0; +} + +static void rdacm20_shutdown(struct i2c_client *client) +{ + struct rdacm20_device *dev = i2c_to_rdacm20(client); + + /* make sure stream off during shutdown (reset/reboot) */ + rdacm20_s_stream(&dev->sd, 0); +} + +static const struct of_device_id rdacm20_of_ids[] = { + { .compatible = "imi,rdacm20", }, + { } +}; +MODULE_DEVICE_TABLE(of, rdacm20_of_ids); + +static struct i2c_driver rdacm20_i2c_driver = { + .driver = { + .name = "rdacm20", + .of_match_table = rdacm20_of_ids, + }, + .probe_new = rdacm20_probe, + .remove = rdacm20_remove, + .shutdown = rdacm20_shutdown, +}; + +module_i2c_driver(rdacm20_i2c_driver); + +MODULE_DESCRIPTION("GMSL Camera driver for RDACM20"); +MODULE_AUTHOR("Vladimir Barinov"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From b98158d837efa0b2f2f59ee2ff77f4791f978d74 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 1 Jul 2020 08:21:38 +0200 Subject: media: v4l2-async: Accept endpoints and devices for fwnode matching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fwnode matching was designed to match on nodes corresponding to a device. Some drivers, however, needed to match on endpoints, and have passed endpoint fwnodes to v4l2-async. This works when both the subdev and the notifier use the same fwnode types (endpoint or device), but makes drivers that use different types incompatible. Fix this by extending the fwnode match to handle fwnodes of different types. When the types (deduced from the presence of remote endpoints) are different, retrieve the device fwnode for the side that provides an endpoint fwnode, and compare it with the device fwnode provided by the other side. This allows interoperability between all drivers, regardless of which type of fwnode they use for matching. Signed-off-by: Laurent Pinchart Tested-by: Lad Prabhakar Reviewed-by: Kieran Bingham Reviewed-by: Jacopo Mondi Tested-by: Niklas Söderlund Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 43 +++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 8bde33c21ce4..e9c3d41c9449 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -71,7 +71,48 @@ static bool match_devname(struct v4l2_subdev *sd, static bool match_fwnode(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { - return sd->fwnode == asd->match.fwnode; + struct fwnode_handle *other_fwnode; + struct fwnode_handle *dev_fwnode; + bool asd_fwnode_is_ep; + bool sd_fwnode_is_ep; + + /* + * Both the subdev and the async subdev can provide either an endpoint + * fwnode or a device fwnode. Start with the simple case of direct + * fwnode matching. + */ + if (sd->fwnode == asd->match.fwnode) + return true; + + /* + * Otherwise, check if the sd fwnode and the asd fwnode refer to an + * endpoint or a device. If they're of the same type, there's no match. + * Technically speaking this checks if the nodes refer to a connected + * endpoint, which is the simplest check that works for both OF and + * ACPI. This won't make a difference, as drivers should not try to + * match unconnected endpoints. + */ + sd_fwnode_is_ep = fwnode_graph_is_endpoint(sd->fwnode); + asd_fwnode_is_ep = fwnode_graph_is_endpoint(asd->match.fwnode); + + if (sd_fwnode_is_ep == asd_fwnode_is_ep) + return false; + + /* + * The sd and asd fwnodes are of different types. Get the device fwnode + * parent of the endpoint fwnode, and compare it with the other fwnode. + */ + if (sd_fwnode_is_ep) { + dev_fwnode = fwnode_graph_get_port_parent(sd->fwnode); + other_fwnode = asd->match.fwnode; + } else { + dev_fwnode = fwnode_graph_get_port_parent(asd->match.fwnode); + other_fwnode = sd->fwnode; + } + + fwnode_handle_put(dev_fwnode); + + return dev_fwnode == other_fwnode; } static bool match_custom(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) -- cgit v1.2.3 From 3e33392a9561fd64515049317041646ab3bf32aa Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 1 Jul 2020 08:21:39 +0200 Subject: media: v4l2-async: Pass notifier pointer to match functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The notifier is useful to match functions to access information about the device matching a subdev. This will be used to print messages using the correct struct device and driver name. Signed-off-by: Laurent Pinchart Tested-by: Lad Prabhakar Reviewed-by: Kieran Bingham Reviewed-by: Jacopo Mondi Tested-by: Niklas Söderlund Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index e9c3d41c9449..60833822ee89 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -50,7 +50,8 @@ static int v4l2_async_notifier_call_complete(struct v4l2_async_notifier *n) return n->ops->complete(n); } -static bool match_i2c(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) +static bool match_i2c(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { #if IS_ENABLED(CONFIG_I2C) struct i2c_client *client = i2c_verify_client(sd->dev); @@ -63,13 +64,14 @@ static bool match_i2c(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) #endif } -static bool match_devname(struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) +static bool match_devname(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { return !strcmp(asd->match.device_name, dev_name(sd->dev)); } -static bool match_fwnode(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) +static bool match_fwnode(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { struct fwnode_handle *other_fwnode; struct fwnode_handle *dev_fwnode; @@ -115,7 +117,8 @@ static bool match_fwnode(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) return dev_fwnode == other_fwnode; } -static bool match_custom(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) +static bool match_custom(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { if (!asd->match.custom.match) /* Match always */ @@ -132,7 +135,8 @@ static struct v4l2_async_subdev * v4l2_async_find_match(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd) { - bool (*match)(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd); + bool (*match)(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd); struct v4l2_async_subdev *asd; list_for_each_entry(asd, ¬ifier->waiting, list) { @@ -157,7 +161,7 @@ v4l2_async_find_match(struct v4l2_async_notifier *notifier, } /* match cannot be NULL here */ - if (match(sd, asd)) + if (match(notifier, sd, asd)) return asd; } -- cgit v1.2.3 From e80cdf0a3843fe570c891cb976feb0c136b19fb7 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 1 Jul 2020 08:21:40 +0200 Subject: media: v4l2-async: Log message in case of heterogeneous fwnode match MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a notifier supplies a device fwnode and a subdev supplies an endpoint fwnode, incorrect matches may occur if multiple subdevs correspond to the same device fwnode. This can't be handled transparently in the framework, and requires the notifier to switch to endpoint fwnodes. Log a message to notify of this problem. A second message is added to help accelerating the transition to endpoint matching. Signed-off-by: Laurent Pinchart Tested-by: Lad Prabhakar Reviewed-by: Kieran Bingham Reviewed-by: Jacopo Mondi Tested-by: Niklas Söderlund Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 60833822ee89..e3ab003a6c85 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -77,6 +77,7 @@ static bool match_fwnode(struct v4l2_async_notifier *notifier, struct fwnode_handle *dev_fwnode; bool asd_fwnode_is_ep; bool sd_fwnode_is_ep; + struct device *dev; /* * Both the subdev and the async subdev can provide either an endpoint @@ -114,7 +115,28 @@ static bool match_fwnode(struct v4l2_async_notifier *notifier, fwnode_handle_put(dev_fwnode); - return dev_fwnode == other_fwnode; + if (dev_fwnode != other_fwnode) + return false; + + /* + * We have a heterogeneous match. Retrieve the struct device of the side + * that matched on a device fwnode to print its driver name. + */ + if (sd_fwnode_is_ep) + dev = notifier->v4l2_dev ? notifier->v4l2_dev->dev + : notifier->sd->dev; + else + dev = sd->dev; + + if (dev && dev->driver) { + if (sd_fwnode_is_ep) + dev_warn(dev, "Driver %s uses device fwnode, incorrect match may occur\n", + dev->driver->name); + dev_notice(dev, "Consider updating driver %s to match on endpoints\n", + dev->driver->name); + } + + return true; } static bool match_custom(struct v4l2_async_notifier *notifier, -- cgit v1.2.3 From 2856bbc55bda13d41eab904234be30e52ceeeae9 Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Fri, 17 Jul 2020 09:34:31 +0200 Subject: media: sta2x11: use generic power management With legacy PM, drivers themselves were responsible for managing the device's power states and takes care of register states. After upgrading to the generic structure, PCI core will take care of required tasks and drivers should do only device-specific operations. Thus, there is no need to call the PCI helper functions like pci_enable_device(), pci_save/restore_sate(), etc. Compile-tested only. Signed-off-by: Vaibhav Gupta Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/sta2x11/sta2x11_vip.c | 63 ++++++++------------------------- 1 file changed, 15 insertions(+), 48 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index 798574cfad35..0fdb0fd6e764 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -1167,21 +1167,18 @@ static void sta2x11_vip_remove_one(struct pci_dev *pdev) */ } -#ifdef CONFIG_PM - /** * sta2x11_vip_suspend - set device into power save mode - * @pdev: PCI device - * @state: new state of device + * @dev_d: PCI device * * all relevant registers are saved and an attempt to set a new state is made. * * return value: 0 always indicate success, * even if device could not be disabled. (workaround for hardware problem) */ -static int sta2x11_vip_suspend(struct pci_dev *pdev, pm_message_t state) +static int __maybe_unused sta2x11_vip_suspend(struct device *dev_d) { - struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d); struct sta2x11_vip *vip = container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); unsigned long flags; @@ -1198,15 +1195,8 @@ static int sta2x11_vip_suspend(struct pci_dev *pdev, pm_message_t state) vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i] = reg_read(vip, registers_to_save[i]); spin_unlock_irqrestore(&vip->slock, flags); - /* save pci state */ - pci_save_state(pdev); - if (pci_set_power_state(pdev, pci_choose_state(pdev, state))) { - /* - * do not call pci_disable_device on sta2x11 because it - * break all other Bus masters on this EP - */ - vip->disabled = 1; - } + + vip->disabled = 1; pr_info("VIP: suspend\n"); return 0; @@ -1214,45 +1204,23 @@ static int sta2x11_vip_suspend(struct pci_dev *pdev, pm_message_t state) /** * sta2x11_vip_resume - resume device operation - * @pdev : PCI device - * - * re-enable device, set PCI state to powered and restore registers. - * resume normal device operation afterwards. + * @dev_d : PCI device * * return value: 0, no error. * * other, could not set device to power on state. */ -static int sta2x11_vip_resume(struct pci_dev *pdev) +static int __maybe_unused sta2x11_vip_resume(struct device *dev_d) { - struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d); struct sta2x11_vip *vip = container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); unsigned long flags; - int ret, i; + int i; pr_info("VIP: resume\n"); - /* restore pci state */ - if (vip->disabled) { - ret = pci_enable_device(pdev); - if (ret) { - pr_warn("VIP: Can't enable device.\n"); - return ret; - } - vip->disabled = 0; - } - ret = pci_set_power_state(pdev, PCI_D0); - if (ret) { - /* - * do not call pci_disable_device on sta2x11 because it - * break all other Bus masters on this EP - */ - pr_warn("VIP: Can't enable device.\n"); - vip->disabled = 1; - return ret; - } - pci_restore_state(pdev); + vip->disabled = 0; spin_lock_irqsave(&vip->slock, flags); for (i = 1; i < SAVE_COUNT; i++) @@ -1266,22 +1234,21 @@ static int sta2x11_vip_resume(struct pci_dev *pdev) return 0; } -#endif - static const struct pci_device_id sta2x11_vip_pci_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_VIP)}, {0,} }; +static SIMPLE_DEV_PM_OPS(sta2x11_vip_pm_ops, + sta2x11_vip_suspend, + sta2x11_vip_resume); + static struct pci_driver sta2x11_vip_driver = { .name = KBUILD_MODNAME, .probe = sta2x11_vip_init_one, .remove = sta2x11_vip_remove_one, .id_table = sta2x11_vip_pci_tbl, -#ifdef CONFIG_PM - .suspend = sta2x11_vip_suspend, - .resume = sta2x11_vip_resume, -#endif + .driver.pm = &sta2x11_vip_pm_ops, }; static int __init sta2x11_vip_init_module(void) -- cgit v1.2.3 From d6f6582890a43e2a88364aeeeb5414d75f7553d5 Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Fri, 17 Jul 2020 09:34:32 +0200 Subject: media: cx23885: use generic power management The .suspend() and .resume() callbacks are not defined for this driver. Still, their power management structure follows the legacy framework. To bring it under the generic framework, simply remove the binding of callbacks from struct "pci_driver". Compile-tested only. Signed-off-by: Vaibhav Gupta Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx23885/cx23885-core.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 7e0b0b7cc2a3..4b0c53f61fb7 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -2235,9 +2235,6 @@ static struct pci_driver cx23885_pci_driver = { .id_table = cx23885_pci_tbl, .probe = cx23885_initdev, .remove = cx23885_finidev, - /* TODO */ - .suspend = NULL, - .resume = NULL, }; static int __init cx23885_init(void) -- cgit v1.2.3 From fc1f0e67f68f78da8426632e7ff3703b93148dba Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Fri, 17 Jul 2020 09:34:33 +0200 Subject: media: cx25821: use generic power management The .suspend() and .resume() callbacks are not defined for this driver. Still, their power management structure follows the legacy framework. To bring it under the generic framework, simply remove the binding of callbacks from struct "pci_driver". Compile-tested only. Signed-off-by: Vaibhav Gupta Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx25821/cx25821-core.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c index 41be22ce66f3..55018d9e439f 100644 --- a/drivers/media/pci/cx25821/cx25821-core.c +++ b/drivers/media/pci/cx25821/cx25821-core.c @@ -1374,9 +1374,6 @@ static struct pci_driver cx25821_pci_driver = { .id_table = cx25821_pci_tbl, .probe = cx25821_initdev, .remove = cx25821_finidev, - /* TODO */ - .suspend = NULL, - .resume = NULL, }; static int __init cx25821_init(void) -- cgit v1.2.3 From a28b2ed9ad0551a18aaa2fa6ba8dd3d3e8cfb27f Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Fri, 17 Jul 2020 09:34:34 +0200 Subject: media: cx88: use generic power management With legacy PM, drivers themselves were responsible for managing the device's power states and takes care of register states. After upgrading to the generic structure, PCI core will take care of required tasks and drivers should do only device-specific operations. The driver was invoking PCI helper functions like pci_save/restore_state(), pci_enable/disable_device() and pci_set_power_state(), which is not recommended. Compile-tested only. [hverkuil: fixes checkpatch alignment warning] Signed-off-by: Vaibhav Gupta Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx88/cx88-video.c | 60 ++++++++++--------------------------- 1 file changed, 16 insertions(+), 44 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c index ba0e9660a047..e7fd7516787c 100644 --- a/drivers/media/pci/cx88/cx88-video.c +++ b/drivers/media/pci/cx88/cx88-video.c @@ -385,8 +385,7 @@ static int start_video_dma(struct cx8800_dev *dev, return 0; } -#ifdef CONFIG_PM -static int stop_video_dma(struct cx8800_dev *dev) +static int __maybe_unused stop_video_dma(struct cx8800_dev *dev) { struct cx88_core *core = dev->core; @@ -402,8 +401,8 @@ static int stop_video_dma(struct cx8800_dev *dev) return 0; } -static int restart_video_queue(struct cx8800_dev *dev, - struct cx88_dmaqueue *q) +static int __maybe_unused restart_video_queue(struct cx8800_dev *dev, + struct cx88_dmaqueue *q) { struct cx88_buffer *buf; @@ -415,7 +414,6 @@ static int restart_video_queue(struct cx8800_dev *dev, } return 0; } -#endif /* ------------------------------------------------------------------ */ @@ -1551,10 +1549,9 @@ static void cx8800_finidev(struct pci_dev *pci_dev) kfree(dev); } -#ifdef CONFIG_PM -static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state) +static int __maybe_unused cx8800_suspend(struct device *dev_d) { - struct cx8800_dev *dev = pci_get_drvdata(pci_dev); + struct cx8800_dev *dev = dev_get_drvdata(dev_d); struct cx88_core *core = dev->core; unsigned long flags; @@ -1575,40 +1572,17 @@ static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state) /* FIXME -- shutdown device */ cx88_shutdown(core); - pci_save_state(pci_dev); - if (pci_set_power_state(pci_dev, - pci_choose_state(pci_dev, state)) != 0) { - pci_disable_device(pci_dev); - dev->state.disabled = 1; - } + dev->state.disabled = 1; return 0; } -static int cx8800_resume(struct pci_dev *pci_dev) +static int __maybe_unused cx8800_resume(struct device *dev_d) { - struct cx8800_dev *dev = pci_get_drvdata(pci_dev); + struct cx8800_dev *dev = dev_get_drvdata(dev_d); struct cx88_core *core = dev->core; unsigned long flags; - int err; - if (dev->state.disabled) { - err = pci_enable_device(pci_dev); - if (err) { - pr_err("can't enable device\n"); - return err; - } - - dev->state.disabled = 0; - } - err = pci_set_power_state(pci_dev, PCI_D0); - if (err) { - pr_err("can't set power state\n"); - pci_disable_device(pci_dev); - dev->state.disabled = 1; - - return err; - } - pci_restore_state(pci_dev); + dev->state.disabled = 0; /* FIXME: re-initialize hardware */ cx88_reset(core); @@ -1631,7 +1605,6 @@ static int cx8800_resume(struct pci_dev *pci_dev) return 0; } -#endif /* ----------------------------------------------------------- */ @@ -1647,15 +1620,14 @@ static const struct pci_device_id cx8800_pci_tbl[] = { }; MODULE_DEVICE_TABLE(pci, cx8800_pci_tbl); +static SIMPLE_DEV_PM_OPS(cx8800_pm_ops, cx8800_suspend, cx8800_resume); + static struct pci_driver cx8800_pci_driver = { - .name = "cx8800", - .id_table = cx8800_pci_tbl, - .probe = cx8800_initdev, - .remove = cx8800_finidev, -#ifdef CONFIG_PM - .suspend = cx8800_suspend, - .resume = cx8800_resume, -#endif + .name = "cx8800", + .id_table = cx8800_pci_tbl, + .probe = cx8800_initdev, + .remove = cx8800_finidev, + .driver.pm = &cx8800_pm_ops, }; module_pci_driver(cx8800_pci_driver); -- cgit v1.2.3 From 9c4a8489ab7c29f46beedc5c288a2eacb431c229 Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Fri, 17 Jul 2020 09:34:35 +0200 Subject: media: meye: use generic power management With legacy PM, drivers themselves were responsible for managing the device's power states and takes care of register states. After upgrading to the generic structure, PCI core will take care of required tasks and drivers should do only device-specific operations. The driver was invoking PCI helper functions like pci_save/restore_state() which is not recommended. Compile-tested only. Signed-off-by: Vaibhav Gupta Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/meye/meye.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c index 73e064e6f56d..7fb3b1853b87 100644 --- a/drivers/media/pci/meye/meye.c +++ b/drivers/media/pci/meye/meye.c @@ -1528,19 +1528,16 @@ static const struct v4l2_ctrl_ops meye_ctrl_ops = { .s_ctrl = meye_s_ctrl, }; -#ifdef CONFIG_PM -static int meye_suspend(struct pci_dev *pdev, pm_message_t state) +static int __maybe_unused meye_suspend(struct device *dev) { - pci_save_state(pdev); meye.pm_mchip_mode = meye.mchip_mode; mchip_hic_stop(); mchip_set(MCHIP_MM_INTA, 0x0); return 0; } -static int meye_resume(struct pci_dev *pdev) +static int __maybe_unused meye_resume(struct device *dev) { - pci_restore_state(pdev); pci_write_config_word(meye.mchip_dev, MCHIP_PCI_SOFTRESET_SET, 1); mchip_delay(MCHIP_HIC_CMD, 0); @@ -1562,7 +1559,6 @@ static int meye_resume(struct pci_dev *pdev) } return 0; } -#endif static int meye_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) { @@ -1788,15 +1784,14 @@ static const struct pci_device_id meye_pci_tbl[] = { MODULE_DEVICE_TABLE(pci, meye_pci_tbl); +static SIMPLE_DEV_PM_OPS(meye_pm_ops, meye_suspend, meye_resume); + static struct pci_driver meye_driver = { .name = "meye", .id_table = meye_pci_tbl, .probe = meye_probe, .remove = meye_remove, -#ifdef CONFIG_PM - .suspend = meye_suspend, - .resume = meye_resume, -#endif + .driver.pm = &meye_pm_ops, }; static int __init meye_init(void) -- cgit v1.2.3 From 68e966a3796ed69314ed106a4cea891a4dec48f6 Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Fri, 17 Jul 2020 09:34:36 +0200 Subject: media: tw68: use generic power management With legacy PM, drivers themselves were responsible for managing the device's power states and takes care of register states. After upgrading to the generic structure, PCI core will take care of required tasks and drivers should do only device-specific operations. The driver was invoking PCI helper functions like pci_save/restore_state() which is not recommended. Compile-Tested only. Signed-off-by: Vaibhav Gupta Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/tw68/tw68-core.c | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/tw68/tw68-core.c b/drivers/media/pci/tw68/tw68-core.c index 30c1ea87f0e8..bf15fa7c0ea1 100644 --- a/drivers/media/pci/tw68/tw68-core.c +++ b/drivers/media/pci/tw68/tw68-core.c @@ -359,10 +359,9 @@ static void tw68_finidev(struct pci_dev *pci_dev) v4l2_device_unregister(&dev->v4l2_dev); } -#ifdef CONFIG_PM - -static int tw68_suspend(struct pci_dev *pci_dev , pm_message_t state) +static int __maybe_unused tw68_suspend(struct device *dev_d) { + struct pci_dev *pci_dev = to_pci_dev(dev_d); struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); struct tw68_dev *dev = container_of(v4l2_dev, struct tw68_dev, v4l2_dev); @@ -373,24 +372,19 @@ static int tw68_suspend(struct pci_dev *pci_dev , pm_message_t state) synchronize_irq(pci_dev->irq); - pci_save_state(pci_dev); - pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state)); vb2_discard_done(&dev->vidq); return 0; } -static int tw68_resume(struct pci_dev *pci_dev) +static int __maybe_unused tw68_resume(struct device *dev_d) { - struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d); struct tw68_dev *dev = container_of(v4l2_dev, struct tw68_dev, v4l2_dev); struct tw68_buf *buf; unsigned long flags; - pci_set_power_state(pci_dev, PCI_D0); - pci_restore_state(pci_dev); - /* Do things that are done in tw68_initdev , except of initializing memory structures.*/ @@ -408,19 +402,17 @@ static int tw68_resume(struct pci_dev *pci_dev) return 0; } -#endif /* ----------------------------------------------------------- */ +static SIMPLE_DEV_PM_OPS(tw68_pm_ops, tw68_suspend, tw68_resume); + static struct pci_driver tw68_pci_driver = { - .name = "tw68", - .id_table = tw68_pci_tbl, - .probe = tw68_initdev, - .remove = tw68_finidev, -#ifdef CONFIG_PM - .suspend = tw68_suspend, - .resume = tw68_resume -#endif + .name = "tw68", + .id_table = tw68_pci_tbl, + .probe = tw68_initdev, + .remove = tw68_finidev, + .driver.pm = &tw68_pm_ops, }; module_pci_driver(tw68_pci_driver); -- cgit v1.2.3 From c89ee8ed3289c651f0521ff812c73c82803bdab8 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 17 Jul 2020 05:39:57 +0200 Subject: media: videobuf2: Print videobuf2 buffer state by name For debugging purposes, seeing the state integer representation is really inconvenient. Improve this and be developer-friendly by printing the state name instead. Suggested-by: Nicolas Dufresne Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 35 +++++++++++++++++++------ 1 file changed, 27 insertions(+), 8 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index abaf28e057eb..f544d3393e9d 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -191,6 +191,23 @@ module_param(debug, int, 0644); static void __vb2_queue_cancel(struct vb2_queue *q); static void __enqueue_in_driver(struct vb2_buffer *vb); +static const char *vb2_state_name(enum vb2_buffer_state s) +{ + static const char * const state_names[] = { + [VB2_BUF_STATE_DEQUEUED] = "dequeued", + [VB2_BUF_STATE_IN_REQUEST] = "in request", + [VB2_BUF_STATE_PREPARING] = "preparing", + [VB2_BUF_STATE_QUEUED] = "queued", + [VB2_BUF_STATE_ACTIVE] = "active", + [VB2_BUF_STATE_DONE] = "done", + [VB2_BUF_STATE_ERROR] = "error", + }; + + if ((unsigned int)(s) < ARRAY_SIZE(state_names)) + return state_names[s]; + return "unknown"; +} + /* * __vb2_buf_mem_alloc() - allocate video memory for the given buffer */ @@ -1015,8 +1032,8 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) */ vb->cnt_buf_done++; #endif - dprintk(q, 4, "done processing on buffer %d, state: %d\n", - vb->index, state); + dprintk(q, 4, "done processing on buffer %d, state: %s\n", + vb->index, vb2_state_name(state)); if (state != VB2_BUF_STATE_QUEUED) __vb2_buf_mem_finish(vb); @@ -1490,8 +1507,8 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb) vb = q->bufs[index]; if (vb->state != VB2_BUF_STATE_DEQUEUED) { - dprintk(q, 1, "invalid buffer state %d\n", - vb->state); + dprintk(q, 1, "invalid buffer state %s\n", + vb2_state_name(vb->state)); return -EINVAL; } if (vb->prepared) { @@ -1670,7 +1687,8 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, dprintk(q, 1, "buffer still being prepared\n"); return -EINVAL; default: - dprintk(q, 1, "invalid buffer state %d\n", vb->state); + dprintk(q, 1, "invalid buffer state %s\n", + vb2_state_name(vb->state)); return -EINVAL; } @@ -1884,7 +1902,8 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb, dprintk(q, 3, "returning done buffer with errors\n"); break; default: - dprintk(q, 1, "invalid buffer state\n"); + dprintk(q, 1, "invalid buffer state %s\n", + vb2_state_name(vb->state)); return -EINVAL; } @@ -1915,8 +1934,8 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb, media_request_put(vb->request); vb->request = NULL; - dprintk(q, 2, "dqbuf of buffer %d, with state %d\n", - vb->index, vb->state); + dprintk(q, 2, "dqbuf of buffer %d, state: %s\n", + vb->index, vb2_state_name(vb->state)); return 0; -- cgit v1.2.3 From 7aa87db5b81b0281c7493ab58e4f702525e85fe4 Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Mon, 20 Jul 2020 12:27:01 +0200 Subject: media: saa7164: use generic power management The .suspend() and .resume() callbacks are not defined for this driver. Still, their power management structure follows the legacy framework. To bring it under the generic framework, simply remove the binding of callbacks from struct "pci_driver". Compile-tested only. Signed-off-by: Vaibhav Gupta Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/saa7164/saa7164-core.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c index 126d085be9a7..4b637891b79a 100644 --- a/drivers/media/pci/saa7164/saa7164-core.c +++ b/drivers/media/pci/saa7164/saa7164-core.c @@ -1539,9 +1539,6 @@ static struct pci_driver saa7164_pci_driver = { .id_table = saa7164_pci_tbl, .probe = saa7164_initdev, .remove = saa7164_finidev, - /* TODO */ - .suspend = NULL, - .resume = NULL, }; static int __init saa7164_init(void) -- cgit v1.2.3 From e3083c3f369700cd1eec3de93b8d8ec0918ff778 Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Tue, 21 Jul 2020 06:47:45 +0200 Subject: media: cafe-driver: use generic power management Drivers using legacy PM have to manage PCI states and device's PM states themselves. They also need to take care of configuration registers. With improved and powerful support of generic PM, PCI Core takes care of above mentioned, device-independent, jobs. This driver makes use of PCI helper functions like pci_save/restore_state() and pci_enable/disable_device() to do required operations. In generic mode, they are no longer needed. Change function parameter in both .suspend() and .resume() to "struct device*" type. Compile-tested only. Signed-off-by: Vaibhav Gupta Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/marvell-ccic/cafe-driver.c | 31 +++++------------------ drivers/media/platform/marvell-ccic/mcam-core.c | 3 --- drivers/media/platform/marvell-ccic/mcam-core.h | 2 -- 3 files changed, 7 insertions(+), 29 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c index 9a09a10a3631..58b9915ac7a4 100644 --- a/drivers/media/platform/marvell-ccic/cafe-driver.c +++ b/drivers/media/platform/marvell-ccic/cafe-driver.c @@ -604,44 +604,28 @@ static void cafe_pci_remove(struct pci_dev *pdev) } -#ifdef CONFIG_PM /* * Basic power management. */ -static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state) +static int __maybe_unused cafe_pci_suspend(struct device *dev) { - struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); struct cafe_camera *cam = to_cam(v4l2_dev); - int ret; - ret = pci_save_state(pdev); - if (ret) - return ret; mccic_suspend(&cam->mcam); - pci_disable_device(pdev); return 0; } -static int cafe_pci_resume(struct pci_dev *pdev) +static int __maybe_unused cafe_pci_resume(struct device *dev) { - struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); struct cafe_camera *cam = to_cam(v4l2_dev); - int ret = 0; - pci_restore_state(pdev); - ret = pci_enable_device(pdev); - - if (ret) { - cam_warn(cam, "Unable to re-enable device on resume!\n"); - return ret; - } cafe_ctlr_init(&cam->mcam); return mccic_resume(&cam->mcam); } -#endif /* CONFIG_PM */ - static const struct pci_device_id cafe_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_88ALP01_CCIC) }, @@ -650,15 +634,14 @@ static const struct pci_device_id cafe_ids[] = { MODULE_DEVICE_TABLE(pci, cafe_ids); +static SIMPLE_DEV_PM_OPS(cafe_pci_pm_ops, cafe_pci_suspend, cafe_pci_resume); + static struct pci_driver cafe_pci_driver = { .name = "cafe1000-ccic", .id_table = cafe_ids, .probe = cafe_pci_probe, .remove = cafe_pci_remove, -#ifdef CONFIG_PM - .suspend = cafe_pci_suspend, - .resume = cafe_pci_resume, -#endif + .driver.pm = &cafe_pci_pm_ops, }; diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 326e79b8531c..3d4242b8182b 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -1969,8 +1969,6 @@ EXPORT_SYMBOL_GPL(mccic_shutdown); /* * Power management */ -#ifdef CONFIG_PM - void mccic_suspend(struct mcam_camera *cam) { mutex_lock(&cam->s_mutex); @@ -2017,7 +2015,6 @@ int mccic_resume(struct mcam_camera *cam) return ret; } EXPORT_SYMBOL_GPL(mccic_resume); -#endif /* CONFIG_PM */ MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Jonathan Corbet "); diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index 2e3a7567a76a..b55545822fd2 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -236,10 +236,8 @@ static inline void mcam_reg_set_bit(struct mcam_camera *cam, int mccic_register(struct mcam_camera *cam); int mccic_irq(struct mcam_camera *cam, unsigned int irqs); void mccic_shutdown(struct mcam_camera *cam); -#ifdef CONFIG_PM void mccic_suspend(struct mcam_camera *cam); int mccic_resume(struct mcam_camera *cam); -#endif /* * Register definitions for the m88alp01 camera interface. Offsets in bytes -- cgit v1.2.3 From 43663dc4f4300e6a4ff4dade9a17cef42e79232c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 21 Jul 2020 23:32:29 +0200 Subject: media: meye: fix missing pm_mchip_mode field After commit 9c4a8489ab7c ("media: meye: use generic power management") the pm_mchip_mode field should now always be present, even if CONFIG_PM is not set. Signed-off-by: Hans Verkuil Reported-by: kernel test robot Fixes: 9c4a8489ab7c ("media: meye: use generic power management") Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/meye/meye.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/meye/meye.h b/drivers/media/pci/meye/meye.h index c957d9b55f89..5fa6552cf93d 100644 --- a/drivers/media/pci/meye/meye.h +++ b/drivers/media/pci/meye/meye.h @@ -305,9 +305,7 @@ struct meye { u16 colour; struct meye_params params; /* additional parameters */ unsigned long in_use; /* set to 1 if the device is in use */ -#ifdef CONFIG_PM u8 pm_mchip_mode; /* old mchip mode */ -#endif }; #endif -- cgit v1.2.3 From dd4eddc4ba31fbf4554fc5fa12d3a553b50e1469 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 22 Jul 2020 14:39:14 +0200 Subject: media: mtk-mdp: Fix a refcounting bug on error in init We need to call of_node_put(comp->dev_node); on the error paths in this function. Fixes: c8eb2d7e8202 ("[media] media: Add Mediatek MDP Driver") Signed-off-by: Dan Carpenter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-mdp/mtk_mdp_comp.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c index 362fff924aef..b3426a551bea 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c @@ -57,6 +57,7 @@ int mtk_mdp_comp_init(struct device *dev, struct device_node *node, { struct device_node *larb_node; struct platform_device *larb_pdev; + int ret; int i; comp->dev_node = of_node_get(node); @@ -67,8 +68,8 @@ int mtk_mdp_comp_init(struct device *dev, struct device_node *node, if (IS_ERR(comp->clk[i])) { if (PTR_ERR(comp->clk[i]) != -EPROBE_DEFER) dev_err(dev, "Failed to get clock\n"); - - return PTR_ERR(comp->clk[i]); + ret = PTR_ERR(comp->clk[i]); + goto put_dev; } /* Only RDMA needs two clocks */ @@ -87,20 +88,27 @@ int mtk_mdp_comp_init(struct device *dev, struct device_node *node, if (!larb_node) { dev_err(dev, "Missing mediadek,larb phandle in %pOF node\n", node); - return -EINVAL; + ret = -EINVAL; + goto put_dev; } larb_pdev = of_find_device_by_node(larb_node); if (!larb_pdev) { dev_warn(dev, "Waiting for larb device %pOF\n", larb_node); of_node_put(larb_node); - return -EPROBE_DEFER; + ret = -EPROBE_DEFER; + goto put_dev; } of_node_put(larb_node); comp->larb_dev = &larb_pdev->dev; return 0; + +put_dev: + of_node_put(comp->dev_node); + + return ret; } void mtk_mdp_comp_deinit(struct device *dev, struct mtk_mdp_comp *comp) -- cgit v1.2.3 From 670f7ef5f0fccd5b6dd7da80a0f7ec6b6cf9aafa Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 22 Jul 2020 17:16:30 +0200 Subject: media: radio: remove redundant assignment to variable retval The variable retval is being initialized with a value that is never read and it is being updated later with a new value. The initialization is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/si4713/radio-usb-si4713.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/radio/si4713/radio-usb-si4713.c b/drivers/media/radio/si4713/radio-usb-si4713.c index 33274189c83c..2cf36c8abdde 100644 --- a/drivers/media/radio/si4713/radio-usb-si4713.c +++ b/drivers/media/radio/si4713/radio-usb-si4713.c @@ -414,7 +414,7 @@ static int usb_si4713_probe(struct usb_interface *intf, struct si4713_usb_device *radio; struct i2c_adapter *adapter; struct v4l2_subdev *sd; - int retval = -ENOMEM; + int retval; dev_info(&intf->dev, "Si4713 development board discovered: (%04X:%04X)\n", id->idVendor, id->idProduct); -- cgit v1.2.3 From 602649eadaa0c977e362e641f51ec306bc1d365d Mon Sep 17 00:00:00 2001 From: Evgeny Novikov Date: Thu, 23 Jul 2020 19:04:53 +0200 Subject: media: davinci: vpif_capture: fix potential double free In case of errors vpif_probe_complete() releases memory for vpif_obj.sd and unregisters the V4L2 device. But then this is done again by vpif_probe() itself. The patch removes the cleaning from vpif_probe_complete(). Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Evgeny Novikov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpif_capture.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index d9ec439faefa..72a0e94e2e21 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -1482,8 +1482,6 @@ probe_out: /* Unregister video device */ video_unregister_device(&ch->video_dev); } - kfree(vpif_obj.sd); - v4l2_device_unregister(&vpif_obj.v4l2_dev); return err; } -- cgit v1.2.3 From f45882cfb152f5d3a421fd58f177f227e44843b9 Mon Sep 17 00:00:00 2001 From: Evgeny Novikov Date: Thu, 23 Jul 2020 19:46:51 +0200 Subject: media: camss: fix memory leaks on error handling paths in probe camss_probe() does not free camss on error handling paths. The patch introduces an additional error label for this purpose. Besides, it removes call of v4l2_async_notifier_cleanup() from camss_of_parse_ports() since its caller, camss_probe(), cleans up all its resources itself. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Evgeny Novikov Co-developed-by: Anton Vasilyev Signed-off-by: Anton Vasilyev Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 3fdc9f964a3c..2483641799df 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -504,7 +504,6 @@ static int camss_of_parse_ports(struct camss *camss) return num_subdevs; err_cleanup: - v4l2_async_notifier_cleanup(&camss->notifier); of_node_put(node); return ret; } @@ -835,29 +834,38 @@ static int camss_probe(struct platform_device *pdev) camss->csid_num = 4; camss->vfe_num = 2; } else { - return -EINVAL; + ret = -EINVAL; + goto err_free; } camss->csiphy = devm_kcalloc(dev, camss->csiphy_num, sizeof(*camss->csiphy), GFP_KERNEL); - if (!camss->csiphy) - return -ENOMEM; + if (!camss->csiphy) { + ret = -ENOMEM; + goto err_free; + } camss->csid = devm_kcalloc(dev, camss->csid_num, sizeof(*camss->csid), GFP_KERNEL); - if (!camss->csid) - return -ENOMEM; + if (!camss->csid) { + ret = -ENOMEM; + goto err_free; + } camss->vfe = devm_kcalloc(dev, camss->vfe_num, sizeof(*camss->vfe), GFP_KERNEL); - if (!camss->vfe) - return -ENOMEM; + if (!camss->vfe) { + ret = -ENOMEM; + goto err_free; + } v4l2_async_notifier_init(&camss->notifier); num_subdevs = camss_of_parse_ports(camss); - if (num_subdevs < 0) - return num_subdevs; + if (num_subdevs < 0) { + ret = num_subdevs; + goto err_cleanup; + } ret = camss_init_subdevices(camss); if (ret < 0) @@ -936,6 +944,8 @@ err_register_entities: v4l2_device_unregister(&camss->v4l2_dev); err_cleanup: v4l2_async_notifier_cleanup(&camss->notifier); +err_free: + kfree(camss); return ret; } -- cgit v1.2.3