From d64685e129c0a3db7b5d7cfaee6661eca9cbbdc7 Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Thu, 12 Dec 2024 10:17:30 +0100 Subject: media: stm32: dcmipp: rename dcmipp_parallel into dcmipp_input In preparation of the introduction of dcmipp csi input support, rename the dcmipp_parallel subdev into a generic dcmipp_input which will be in charge of handling both parallel input & csi input. Only structures / variables / functions and file naming are changed without any functional modifications. Signed-off-by: Alain Volmat Signed-off-by: Hans Verkuil --- .../media/platform/st/stm32/stm32-dcmipp/Makefile | 2 +- .../platform/st/stm32/stm32-dcmipp/dcmipp-common.h | 4 +- .../platform/st/stm32/stm32-dcmipp/dcmipp-core.c | 12 +- .../platform/st/stm32/stm32-dcmipp/dcmipp-input.c | 454 +++++++++++++++++++++ .../st/stm32/stm32-dcmipp/dcmipp-parallel.c | 454 --------------------- 5 files changed, 463 insertions(+), 463 deletions(-) create mode 100644 drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c delete mode 100644 drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile index 8920d9388a21..159105fb40b8 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -stm32-dcmipp-y := dcmipp-core.o dcmipp-common.o dcmipp-parallel.o dcmipp-byteproc.o dcmipp-bytecap.o +stm32-dcmipp-y := dcmipp-core.o dcmipp-common.o dcmipp-input.o dcmipp-byteproc.o dcmipp-bytecap.o obj-$(CONFIG_VIDEO_STM32_DCMIPP) += stm32-dcmipp.o diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h index 7a7cf43baf24..fe5f97233f5e 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h @@ -199,11 +199,11 @@ static inline void __reg_clear(struct device *dev, void __iomem *base, u32 reg, } /* DCMIPP subdev init / release entry points */ -struct dcmipp_ent_device *dcmipp_par_ent_init(struct device *dev, +struct dcmipp_ent_device *dcmipp_inp_ent_init(struct device *dev, const char *entity_name, struct v4l2_device *v4l2_dev, void __iomem *regs); -void dcmipp_par_ent_release(struct dcmipp_ent_device *ved); +void dcmipp_inp_ent_release(struct dcmipp_ent_device *ved); struct dcmipp_ent_device * dcmipp_byteproc_ent_init(struct device *dev, const char *entity_name, struct v4l2_device *v4l2_dev, void __iomem *regs); diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c index 3806f7c6e2fe..97590079bb5a 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c @@ -95,9 +95,9 @@ struct dcmipp_pipeline_config { static const struct dcmipp_ent_config stm32mp13_ent_config[] = { { - .name = "dcmipp_parallel", - .init = dcmipp_par_ent_init, - .release = dcmipp_par_ent_release, + .name = "dcmipp_input", + .init = dcmipp_inp_ent_init, + .release = dcmipp_inp_ent_release, }, { .name = "dcmipp_dump_postproc", @@ -111,12 +111,12 @@ static const struct dcmipp_ent_config stm32mp13_ent_config[] = { }, }; -#define ID_PARALLEL 0 +#define ID_INPUT 0 #define ID_DUMP_BYTEPROC 1 #define ID_DUMP_CAPTURE 2 static const struct dcmipp_ent_link stm32mp13_ent_links[] = { - DCMIPP_ENT_LINK(ID_PARALLEL, 1, ID_DUMP_BYTEPROC, 0, + DCMIPP_ENT_LINK(ID_INPUT, 1, ID_DUMP_BYTEPROC, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), DCMIPP_ENT_LINK(ID_DUMP_BYTEPROC, 1, ID_DUMP_CAPTURE, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), @@ -309,7 +309,7 @@ static int dcmipp_graph_notify_bound(struct v4l2_async_notifier *notifier, } /* Parallel input device detected, connect it to parallel subdev */ - sink = dcmipp->entity[ID_PARALLEL]; + sink = dcmipp->entity[ID_INPUT]; sink->bus.flags = vep.bus.parallel.flags; sink->bus.bus_width = vep.bus.parallel.bus_width; sink->bus.data_shift = vep.bus.parallel.data_shift; diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c new file mode 100644 index 000000000000..689eb4c72e18 --- /dev/null +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for STM32 Digital Camera Memory Interface Pixel Processor + * + * Copyright (C) STMicroelectronics SA 2023 + * Authors: Hugues Fruchet + * Alain Volmat + * for STMicroelectronics. + */ + +#include +#include +#include + +#include "dcmipp-common.h" + +#define DCMIPP_PRCR 0x104 +#define DCMIPP_PRCR_FORMAT_SHIFT 16 +#define DCMIPP_PRCR_FORMAT_YUV422 0x1e +#define DCMIPP_PRCR_FORMAT_RGB565 0x22 +#define DCMIPP_PRCR_FORMAT_RAW8 0x2a +#define DCMIPP_PRCR_FORMAT_G8 0x4a +#define DCMIPP_PRCR_FORMAT_BYTE_STREAM 0x5a +#define DCMIPP_PRCR_ESS BIT(4) +#define DCMIPP_PRCR_PCKPOL BIT(5) +#define DCMIPP_PRCR_HSPOL BIT(6) +#define DCMIPP_PRCR_VSPOL BIT(7) +#define DCMIPP_PRCR_ENABLE BIT(14) +#define DCMIPP_PRCR_SWAPCYCLES BIT(25) + +#define DCMIPP_PRESCR 0x108 +#define DCMIPP_PRESUR 0x10c + +#define IS_SINK(pad) (!(pad)) +#define IS_SRC(pad) ((pad)) + +struct dcmipp_inp_pix_map { + unsigned int code_sink; + unsigned int code_src; + u8 prcr_format; + u8 prcr_swapcycles; +}; + +#define PIXMAP_SINK_SRC_PRCR_SWAP(sink, src, prcr, swap) \ + { \ + .code_sink = MEDIA_BUS_FMT_##sink, \ + .code_src = MEDIA_BUS_FMT_##src, \ + .prcr_format = DCMIPP_PRCR_FORMAT_##prcr, \ + .prcr_swapcycles = swap, \ + } +static const struct dcmipp_inp_pix_map dcmipp_inp_pix_map_list[] = { + /* RGB565 */ + PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_LE, RGB565_2X8_LE, RGB565, 1), + PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_BE, RGB565_2X8_LE, RGB565, 0), + /* YUV422 */ + PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, YUYV8_2X8, YUV422, 1), + PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, UYVY8_2X8, YUV422, 0), + PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, UYVY8_2X8, YUV422, 1), + PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, YUYV8_2X8, YUV422, 0), + PIXMAP_SINK_SRC_PRCR_SWAP(YVYU8_2X8, YVYU8_2X8, YUV422, 1), + PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_2X8, VYUY8_2X8, YUV422, 1), + /* GREY */ + PIXMAP_SINK_SRC_PRCR_SWAP(Y8_1X8, Y8_1X8, G8, 0), + /* Raw Bayer */ + PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR8_1X8, SBGGR8_1X8, RAW8, 0), + PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG8_1X8, SGBRG8_1X8, RAW8, 0), + PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG8_1X8, SGRBG8_1X8, RAW8, 0), + PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB8_1X8, SRGGB8_1X8, RAW8, 0), + /* JPEG */ + PIXMAP_SINK_SRC_PRCR_SWAP(JPEG_1X8, JPEG_1X8, BYTE_STREAM, 0), +}; + +/* + * Search through the pix_map table, skipping two consecutive entry with the + * same code + */ +static inline const struct dcmipp_inp_pix_map *dcmipp_inp_pix_map_by_index + (unsigned int index, + unsigned int pad) +{ + unsigned int i = 0; + u32 prev_code = 0, cur_code; + + while (i < ARRAY_SIZE(dcmipp_inp_pix_map_list)) { + if (IS_SRC(pad)) + cur_code = dcmipp_inp_pix_map_list[i].code_src; + else + cur_code = dcmipp_inp_pix_map_list[i].code_sink; + + if (cur_code == prev_code) { + i++; + continue; + } + prev_code = cur_code; + + if (index == 0) + break; + i++; + index--; + } + + if (i >= ARRAY_SIZE(dcmipp_inp_pix_map_list)) + return NULL; + + return &dcmipp_inp_pix_map_list[i]; +} + +static inline const struct dcmipp_inp_pix_map *dcmipp_inp_pix_map_by_code + (u32 code_sink, u32 code_src) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(dcmipp_inp_pix_map_list); i++) { + if ((dcmipp_inp_pix_map_list[i].code_sink == code_sink && + dcmipp_inp_pix_map_list[i].code_src == code_src) || + (dcmipp_inp_pix_map_list[i].code_sink == code_src && + dcmipp_inp_pix_map_list[i].code_src == code_sink) || + (dcmipp_inp_pix_map_list[i].code_sink == code_sink && + code_src == 0) || + (code_sink == 0 && + dcmipp_inp_pix_map_list[i].code_src == code_src)) + return &dcmipp_inp_pix_map_list[i]; + } + return NULL; +} + +struct dcmipp_inp_device { + struct dcmipp_ent_device ved; + struct v4l2_subdev sd; + struct device *dev; + void __iomem *regs; +}; + +static const struct v4l2_mbus_framefmt fmt_default = { + .width = DCMIPP_FMT_WIDTH_DEFAULT, + .height = DCMIPP_FMT_HEIGHT_DEFAULT, + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .field = V4L2_FIELD_NONE, + .colorspace = DCMIPP_COLORSPACE_DEFAULT, + .ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT, + .quantization = DCMIPP_QUANTIZATION_DEFAULT, + .xfer_func = DCMIPP_XFER_FUNC_DEFAULT, +}; + +static int dcmipp_inp_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + unsigned int i; + + for (i = 0; i < sd->entity.num_pads; i++) { + struct v4l2_mbus_framefmt *mf; + + mf = v4l2_subdev_state_get_format(sd_state, i); + *mf = fmt_default; + } + + return 0; +} + +static int dcmipp_inp_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + const struct dcmipp_inp_pix_map *vpix = + dcmipp_inp_pix_map_by_index(code->index, code->pad); + + if (!vpix) + return -EINVAL; + + code->code = IS_SRC(code->pad) ? vpix->code_src : vpix->code_sink; + + return 0; +} + +static int dcmipp_inp_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + const struct dcmipp_inp_pix_map *vpix; + + if (fse->index) + return -EINVAL; + + /* Only accept code in the pix map table */ + vpix = dcmipp_inp_pix_map_by_code(IS_SINK(fse->pad) ? fse->code : 0, + IS_SRC(fse->pad) ? fse->code : 0); + if (!vpix) + return -EINVAL; + + fse->min_width = DCMIPP_FRAME_MIN_WIDTH; + fse->max_width = DCMIPP_FRAME_MAX_WIDTH; + fse->min_height = DCMIPP_FRAME_MIN_HEIGHT; + fse->max_height = DCMIPP_FRAME_MAX_HEIGHT; + + return 0; +} + +static void dcmipp_inp_adjust_fmt(struct dcmipp_inp_device *inp, + struct v4l2_mbus_framefmt *fmt, __u32 pad) +{ + const struct dcmipp_inp_pix_map *vpix; + + /* Only accept code in the pix map table */ + vpix = dcmipp_inp_pix_map_by_code(IS_SINK(pad) ? fmt->code : 0, + IS_SRC(pad) ? fmt->code : 0); + if (!vpix) + fmt->code = fmt_default.code; + + /* Exclude JPEG if BT656 bus is selected */ + if (vpix && vpix->code_sink == MEDIA_BUS_FMT_JPEG_1X8 && + inp->ved.bus_type == V4L2_MBUS_BT656) + fmt->code = fmt_default.code; + + fmt->width = clamp_t(u32, fmt->width, DCMIPP_FRAME_MIN_WIDTH, + DCMIPP_FRAME_MAX_WIDTH) & ~1; + fmt->height = clamp_t(u32, fmt->height, DCMIPP_FRAME_MIN_HEIGHT, + DCMIPP_FRAME_MAX_HEIGHT) & ~1; + + if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE) + fmt->field = fmt_default.field; + + dcmipp_colorimetry_clamp(fmt); +} + +static int dcmipp_inp_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct dcmipp_inp_device *inp = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *mf; + + if (v4l2_subdev_is_streaming(sd)) + return -EBUSY; + + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); + + /* Set the new format */ + dcmipp_inp_adjust_fmt(inp, &fmt->format, fmt->pad); + + dev_dbg(inp->dev, "%s: format update: old:%dx%d (0x%x, %d, %d, %d, %d) new:%dx%d (0x%x, %d, %d, %d, %d)\n", + inp->sd.name, + /* old */ + mf->width, mf->height, mf->code, + mf->colorspace, mf->quantization, + mf->xfer_func, mf->ycbcr_enc, + /* new */ + fmt->format.width, fmt->format.height, fmt->format.code, + fmt->format.colorspace, fmt->format.quantization, + fmt->format.xfer_func, fmt->format.ycbcr_enc); + + *mf = fmt->format; + + /* When setting the sink format, report that format on the src pad */ + if (IS_SINK(fmt->pad)) { + mf = v4l2_subdev_state_get_format(sd_state, 1); + *mf = fmt->format; + dcmipp_inp_adjust_fmt(inp, mf, 1); + } + + return 0; +} + +static int dcmipp_inp_configure(struct dcmipp_inp_device *inp, + struct v4l2_subdev_state *state) +{ + u32 val = 0; + const struct dcmipp_inp_pix_map *vpix; + struct v4l2_mbus_framefmt *sink_fmt; + struct v4l2_mbus_framefmt *src_fmt; + + /* Set vertical synchronization polarity */ + if (inp->ved.bus.flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) + val |= DCMIPP_PRCR_VSPOL; + + /* Set horizontal synchronization polarity */ + if (inp->ved.bus.flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) + val |= DCMIPP_PRCR_HSPOL; + + /* Set pixel clock polarity */ + if (inp->ved.bus.flags & V4L2_MBUS_PCLK_SAMPLE_RISING) + val |= DCMIPP_PRCR_PCKPOL; + + /* + * BT656 embedded synchronisation bus mode. + * + * Default SAV/EAV mode is supported here with default codes + * SAV=0xff000080 & EAV=0xff00009d. + * With DCMIPP this means LSC=SAV=0x80 & LEC=EAV=0x9d. + */ + if (inp->ved.bus_type == V4L2_MBUS_BT656) { + val |= DCMIPP_PRCR_ESS; + + /* Unmask all codes */ + reg_write(inp, DCMIPP_PRESUR, 0xffffffff);/* FEC:LEC:LSC:FSC */ + + /* Trig on LSC=0x80 & LEC=0x9d codes, ignore FSC and FEC */ + reg_write(inp, DCMIPP_PRESCR, 0xff9d80ff);/* FEC:LEC:LSC:FSC */ + } + + /* Set format */ + sink_fmt = v4l2_subdev_state_get_format(state, 0); + src_fmt = v4l2_subdev_state_get_format(state, 1); + + vpix = dcmipp_inp_pix_map_by_code(sink_fmt->code, src_fmt->code); + if (!vpix) { + dev_err(inp->dev, "Invalid sink/src format configuration\n"); + return -EINVAL; + } + + val |= vpix->prcr_format << DCMIPP_PRCR_FORMAT_SHIFT; + + /* swap cycles */ + if (vpix->prcr_swapcycles) + val |= DCMIPP_PRCR_SWAPCYCLES; + + reg_write(inp, DCMIPP_PRCR, val); + + return 0; +} + +static int dcmipp_inp_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct dcmipp_inp_device *inp = + container_of(sd, struct dcmipp_inp_device, sd); + struct v4l2_subdev *s_subdev; + struct media_pad *s_pad; + int ret; + + /* Get source subdev */ + s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]); + if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity)) + return -EINVAL; + s_subdev = media_entity_to_v4l2_subdev(s_pad->entity); + + ret = dcmipp_inp_configure(inp, state); + if (ret) + return ret; + + /* Enable parallel interface */ + reg_set(inp, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE); + + ret = v4l2_subdev_enable_streams(s_subdev, s_pad->index, BIT_ULL(0)); + if (ret < 0) { + dev_err(inp->dev, + "failed to start source subdev streaming (%d)\n", ret); + return ret; + } + + return 0; +} + +static int dcmipp_inp_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct dcmipp_inp_device *inp = + container_of(sd, struct dcmipp_inp_device, sd); + struct v4l2_subdev *s_subdev; + struct media_pad *s_pad; + int ret; + + /* Get source subdev */ + s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]); + if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity)) + return -EINVAL; + s_subdev = media_entity_to_v4l2_subdev(s_pad->entity); + + ret = v4l2_subdev_disable_streams(s_subdev, s_pad->index, BIT_ULL(0)); + if (ret < 0) { + dev_err(inp->dev, + "failed to stop source subdev streaming (%d)\n", ret); + return ret; + } + + /* Disable parallel interface */ + reg_clear(inp, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE); + + return 0; +} + +static const struct v4l2_subdev_pad_ops dcmipp_inp_pad_ops = { + .enum_mbus_code = dcmipp_inp_enum_mbus_code, + .enum_frame_size = dcmipp_inp_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = dcmipp_inp_set_fmt, + .enable_streams = dcmipp_inp_enable_streams, + .disable_streams = dcmipp_inp_disable_streams, +}; + +static const struct v4l2_subdev_video_ops dcmipp_inp_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_ops dcmipp_inp_ops = { + .pad = &dcmipp_inp_pad_ops, + .video = &dcmipp_inp_video_ops, +}; + +static void dcmipp_inp_release(struct v4l2_subdev *sd) +{ + struct dcmipp_inp_device *inp = + container_of(sd, struct dcmipp_inp_device, sd); + + kfree(inp); +} + +static const struct v4l2_subdev_internal_ops dcmipp_inp_int_ops = { + .init_state = dcmipp_inp_init_state, + .release = dcmipp_inp_release, +}; + +void dcmipp_inp_ent_release(struct dcmipp_ent_device *ved) +{ + struct dcmipp_inp_device *inp = + container_of(ved, struct dcmipp_inp_device, ved); + + dcmipp_ent_sd_unregister(ved, &inp->sd); +} + +struct dcmipp_ent_device *dcmipp_inp_ent_init(struct device *dev, + const char *entity_name, + struct v4l2_device *v4l2_dev, + void __iomem *regs) +{ + struct dcmipp_inp_device *inp; + const unsigned long pads_flag[] = { + MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE, + }; + int ret; + + /* Allocate the inp struct */ + inp = kzalloc(sizeof(*inp), GFP_KERNEL); + if (!inp) + return ERR_PTR(-ENOMEM); + + inp->regs = regs; + + /* Initialize ved and sd */ + ret = dcmipp_ent_sd_register(&inp->ved, &inp->sd, v4l2_dev, + entity_name, MEDIA_ENT_F_VID_IF_BRIDGE, + ARRAY_SIZE(pads_flag), pads_flag, + &dcmipp_inp_int_ops, &dcmipp_inp_ops, + NULL, NULL); + if (ret) { + kfree(inp); + return ERR_PTR(ret); + } + + inp->dev = dev; + + return &inp->ved; +} diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c deleted file mode 100644 index 823c9da336a7..000000000000 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c +++ /dev/null @@ -1,454 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Driver for STM32 Digital Camera Memory Interface Pixel Processor - * - * Copyright (C) STMicroelectronics SA 2023 - * Authors: Hugues Fruchet - * Alain Volmat - * for STMicroelectronics. - */ - -#include -#include -#include - -#include "dcmipp-common.h" - -#define DCMIPP_PRCR 0x104 -#define DCMIPP_PRCR_FORMAT_SHIFT 16 -#define DCMIPP_PRCR_FORMAT_YUV422 0x1e -#define DCMIPP_PRCR_FORMAT_RGB565 0x22 -#define DCMIPP_PRCR_FORMAT_RAW8 0x2a -#define DCMIPP_PRCR_FORMAT_G8 0x4a -#define DCMIPP_PRCR_FORMAT_BYTE_STREAM 0x5a -#define DCMIPP_PRCR_ESS BIT(4) -#define DCMIPP_PRCR_PCKPOL BIT(5) -#define DCMIPP_PRCR_HSPOL BIT(6) -#define DCMIPP_PRCR_VSPOL BIT(7) -#define DCMIPP_PRCR_ENABLE BIT(14) -#define DCMIPP_PRCR_SWAPCYCLES BIT(25) - -#define DCMIPP_PRESCR 0x108 -#define DCMIPP_PRESUR 0x10c - -#define IS_SINK(pad) (!(pad)) -#define IS_SRC(pad) ((pad)) - -struct dcmipp_par_pix_map { - unsigned int code_sink; - unsigned int code_src; - u8 prcr_format; - u8 prcr_swapcycles; -}; - -#define PIXMAP_SINK_SRC_PRCR_SWAP(sink, src, prcr, swap) \ - { \ - .code_sink = MEDIA_BUS_FMT_##sink, \ - .code_src = MEDIA_BUS_FMT_##src, \ - .prcr_format = DCMIPP_PRCR_FORMAT_##prcr, \ - .prcr_swapcycles = swap, \ - } -static const struct dcmipp_par_pix_map dcmipp_par_pix_map_list[] = { - /* RGB565 */ - PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_LE, RGB565_2X8_LE, RGB565, 1), - PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_BE, RGB565_2X8_LE, RGB565, 0), - /* YUV422 */ - PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, YUYV8_2X8, YUV422, 1), - PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, UYVY8_2X8, YUV422, 0), - PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, UYVY8_2X8, YUV422, 1), - PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, YUYV8_2X8, YUV422, 0), - PIXMAP_SINK_SRC_PRCR_SWAP(YVYU8_2X8, YVYU8_2X8, YUV422, 1), - PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_2X8, VYUY8_2X8, YUV422, 1), - /* GREY */ - PIXMAP_SINK_SRC_PRCR_SWAP(Y8_1X8, Y8_1X8, G8, 0), - /* Raw Bayer */ - PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR8_1X8, SBGGR8_1X8, RAW8, 0), - PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG8_1X8, SGBRG8_1X8, RAW8, 0), - PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG8_1X8, SGRBG8_1X8, RAW8, 0), - PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB8_1X8, SRGGB8_1X8, RAW8, 0), - /* JPEG */ - PIXMAP_SINK_SRC_PRCR_SWAP(JPEG_1X8, JPEG_1X8, BYTE_STREAM, 0), -}; - -/* - * Search through the pix_map table, skipping two consecutive entry with the - * same code - */ -static inline const struct dcmipp_par_pix_map *dcmipp_par_pix_map_by_index - (unsigned int index, - unsigned int pad) -{ - unsigned int i = 0; - u32 prev_code = 0, cur_code; - - while (i < ARRAY_SIZE(dcmipp_par_pix_map_list)) { - if (IS_SRC(pad)) - cur_code = dcmipp_par_pix_map_list[i].code_src; - else - cur_code = dcmipp_par_pix_map_list[i].code_sink; - - if (cur_code == prev_code) { - i++; - continue; - } - prev_code = cur_code; - - if (index == 0) - break; - i++; - index--; - } - - if (i >= ARRAY_SIZE(dcmipp_par_pix_map_list)) - return NULL; - - return &dcmipp_par_pix_map_list[i]; -} - -static inline const struct dcmipp_par_pix_map *dcmipp_par_pix_map_by_code - (u32 code_sink, u32 code_src) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(dcmipp_par_pix_map_list); i++) { - if ((dcmipp_par_pix_map_list[i].code_sink == code_sink && - dcmipp_par_pix_map_list[i].code_src == code_src) || - (dcmipp_par_pix_map_list[i].code_sink == code_src && - dcmipp_par_pix_map_list[i].code_src == code_sink) || - (dcmipp_par_pix_map_list[i].code_sink == code_sink && - code_src == 0) || - (code_sink == 0 && - dcmipp_par_pix_map_list[i].code_src == code_src)) - return &dcmipp_par_pix_map_list[i]; - } - return NULL; -} - -struct dcmipp_par_device { - struct dcmipp_ent_device ved; - struct v4l2_subdev sd; - struct device *dev; - void __iomem *regs; -}; - -static const struct v4l2_mbus_framefmt fmt_default = { - .width = DCMIPP_FMT_WIDTH_DEFAULT, - .height = DCMIPP_FMT_HEIGHT_DEFAULT, - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .field = V4L2_FIELD_NONE, - .colorspace = DCMIPP_COLORSPACE_DEFAULT, - .ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT, - .quantization = DCMIPP_QUANTIZATION_DEFAULT, - .xfer_func = DCMIPP_XFER_FUNC_DEFAULT, -}; - -static int dcmipp_par_init_state(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) -{ - unsigned int i; - - for (i = 0; i < sd->entity.num_pads; i++) { - struct v4l2_mbus_framefmt *mf; - - mf = v4l2_subdev_state_get_format(sd_state, i); - *mf = fmt_default; - } - - return 0; -} - -static int dcmipp_par_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - const struct dcmipp_par_pix_map *vpix = - dcmipp_par_pix_map_by_index(code->index, code->pad); - - if (!vpix) - return -EINVAL; - - code->code = IS_SRC(code->pad) ? vpix->code_src : vpix->code_sink; - - return 0; -} - -static int dcmipp_par_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse) -{ - const struct dcmipp_par_pix_map *vpix; - - if (fse->index) - return -EINVAL; - - /* Only accept code in the pix map table */ - vpix = dcmipp_par_pix_map_by_code(IS_SINK(fse->pad) ? fse->code : 0, - IS_SRC(fse->pad) ? fse->code : 0); - if (!vpix) - return -EINVAL; - - fse->min_width = DCMIPP_FRAME_MIN_WIDTH; - fse->max_width = DCMIPP_FRAME_MAX_WIDTH; - fse->min_height = DCMIPP_FRAME_MIN_HEIGHT; - fse->max_height = DCMIPP_FRAME_MAX_HEIGHT; - - return 0; -} - -static void dcmipp_par_adjust_fmt(struct dcmipp_par_device *par, - struct v4l2_mbus_framefmt *fmt, __u32 pad) -{ - const struct dcmipp_par_pix_map *vpix; - - /* Only accept code in the pix map table */ - vpix = dcmipp_par_pix_map_by_code(IS_SINK(pad) ? fmt->code : 0, - IS_SRC(pad) ? fmt->code : 0); - if (!vpix) - fmt->code = fmt_default.code; - - /* Exclude JPEG if BT656 bus is selected */ - if (vpix && vpix->code_sink == MEDIA_BUS_FMT_JPEG_1X8 && - par->ved.bus_type == V4L2_MBUS_BT656) - fmt->code = fmt_default.code; - - fmt->width = clamp_t(u32, fmt->width, DCMIPP_FRAME_MIN_WIDTH, - DCMIPP_FRAME_MAX_WIDTH) & ~1; - fmt->height = clamp_t(u32, fmt->height, DCMIPP_FRAME_MIN_HEIGHT, - DCMIPP_FRAME_MAX_HEIGHT) & ~1; - - if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE) - fmt->field = fmt_default.field; - - dcmipp_colorimetry_clamp(fmt); -} - -static int dcmipp_par_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct dcmipp_par_device *par = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *mf; - - if (v4l2_subdev_is_streaming(sd)) - return -EBUSY; - - mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); - - /* Set the new format */ - dcmipp_par_adjust_fmt(par, &fmt->format, fmt->pad); - - dev_dbg(par->dev, "%s: format update: old:%dx%d (0x%x, %d, %d, %d, %d) new:%dx%d (0x%x, %d, %d, %d, %d)\n", - par->sd.name, - /* old */ - mf->width, mf->height, mf->code, - mf->colorspace, mf->quantization, - mf->xfer_func, mf->ycbcr_enc, - /* new */ - fmt->format.width, fmt->format.height, fmt->format.code, - fmt->format.colorspace, fmt->format.quantization, - fmt->format.xfer_func, fmt->format.ycbcr_enc); - - *mf = fmt->format; - - /* When setting the sink format, report that format on the src pad */ - if (IS_SINK(fmt->pad)) { - mf = v4l2_subdev_state_get_format(sd_state, 1); - *mf = fmt->format; - dcmipp_par_adjust_fmt(par, mf, 1); - } - - return 0; -} - -static int dcmipp_par_configure(struct dcmipp_par_device *par, - struct v4l2_subdev_state *state) -{ - u32 val = 0; - const struct dcmipp_par_pix_map *vpix; - struct v4l2_mbus_framefmt *sink_fmt; - struct v4l2_mbus_framefmt *src_fmt; - - /* Set vertical synchronization polarity */ - if (par->ved.bus.flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) - val |= DCMIPP_PRCR_VSPOL; - - /* Set horizontal synchronization polarity */ - if (par->ved.bus.flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) - val |= DCMIPP_PRCR_HSPOL; - - /* Set pixel clock polarity */ - if (par->ved.bus.flags & V4L2_MBUS_PCLK_SAMPLE_RISING) - val |= DCMIPP_PRCR_PCKPOL; - - /* - * BT656 embedded synchronisation bus mode. - * - * Default SAV/EAV mode is supported here with default codes - * SAV=0xff000080 & EAV=0xff00009d. - * With DCMIPP this means LSC=SAV=0x80 & LEC=EAV=0x9d. - */ - if (par->ved.bus_type == V4L2_MBUS_BT656) { - val |= DCMIPP_PRCR_ESS; - - /* Unmask all codes */ - reg_write(par, DCMIPP_PRESUR, 0xffffffff);/* FEC:LEC:LSC:FSC */ - - /* Trig on LSC=0x80 & LEC=0x9d codes, ignore FSC and FEC */ - reg_write(par, DCMIPP_PRESCR, 0xff9d80ff);/* FEC:LEC:LSC:FSC */ - } - - /* Set format */ - sink_fmt = v4l2_subdev_state_get_format(state, 0); - src_fmt = v4l2_subdev_state_get_format(state, 1); - - vpix = dcmipp_par_pix_map_by_code(sink_fmt->code, src_fmt->code); - if (!vpix) { - dev_err(par->dev, "Invalid sink/src format configuration\n"); - return -EINVAL; - } - - val |= vpix->prcr_format << DCMIPP_PRCR_FORMAT_SHIFT; - - /* swap cycles */ - if (vpix->prcr_swapcycles) - val |= DCMIPP_PRCR_SWAPCYCLES; - - reg_write(par, DCMIPP_PRCR, val); - - return 0; -} - -static int dcmipp_par_enable_streams(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - u32 pad, u64 streams_mask) -{ - struct dcmipp_par_device *par = - container_of(sd, struct dcmipp_par_device, sd); - struct v4l2_subdev *s_subdev; - struct media_pad *s_pad; - int ret; - - /* Get source subdev */ - s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]); - if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity)) - return -EINVAL; - s_subdev = media_entity_to_v4l2_subdev(s_pad->entity); - - ret = dcmipp_par_configure(par, state); - if (ret) - return ret; - - /* Enable parallel interface */ - reg_set(par, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE); - - ret = v4l2_subdev_enable_streams(s_subdev, s_pad->index, BIT_ULL(0)); - if (ret < 0) { - dev_err(par->dev, - "failed to start source subdev streaming (%d)\n", ret); - return ret; - } - - return 0; -} - -static int dcmipp_par_disable_streams(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - u32 pad, u64 streams_mask) -{ - struct dcmipp_par_device *par = - container_of(sd, struct dcmipp_par_device, sd); - struct v4l2_subdev *s_subdev; - struct media_pad *s_pad; - int ret; - - /* Get source subdev */ - s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]); - if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity)) - return -EINVAL; - s_subdev = media_entity_to_v4l2_subdev(s_pad->entity); - - ret = v4l2_subdev_disable_streams(s_subdev, s_pad->index, BIT_ULL(0)); - if (ret < 0) { - dev_err(par->dev, - "failed to stop source subdev streaming (%d)\n", ret); - return ret; - } - - /* Disable parallel interface */ - reg_clear(par, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE); - - return 0; -} - -static const struct v4l2_subdev_pad_ops dcmipp_par_pad_ops = { - .enum_mbus_code = dcmipp_par_enum_mbus_code, - .enum_frame_size = dcmipp_par_enum_frame_size, - .get_fmt = v4l2_subdev_get_fmt, - .set_fmt = dcmipp_par_set_fmt, - .enable_streams = dcmipp_par_enable_streams, - .disable_streams = dcmipp_par_disable_streams, -}; - -static const struct v4l2_subdev_video_ops dcmipp_par_video_ops = { - .s_stream = v4l2_subdev_s_stream_helper, -}; - -static const struct v4l2_subdev_ops dcmipp_par_ops = { - .pad = &dcmipp_par_pad_ops, - .video = &dcmipp_par_video_ops, -}; - -static void dcmipp_par_release(struct v4l2_subdev *sd) -{ - struct dcmipp_par_device *par = - container_of(sd, struct dcmipp_par_device, sd); - - kfree(par); -} - -static const struct v4l2_subdev_internal_ops dcmipp_par_int_ops = { - .init_state = dcmipp_par_init_state, - .release = dcmipp_par_release, -}; - -void dcmipp_par_ent_release(struct dcmipp_ent_device *ved) -{ - struct dcmipp_par_device *par = - container_of(ved, struct dcmipp_par_device, ved); - - dcmipp_ent_sd_unregister(ved, &par->sd); -} - -struct dcmipp_ent_device *dcmipp_par_ent_init(struct device *dev, - const char *entity_name, - struct v4l2_device *v4l2_dev, - void __iomem *regs) -{ - struct dcmipp_par_device *par; - const unsigned long pads_flag[] = { - MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE, - }; - int ret; - - /* Allocate the par struct */ - par = kzalloc(sizeof(*par), GFP_KERNEL); - if (!par) - return ERR_PTR(-ENOMEM); - - par->regs = regs; - - /* Initialize ved and sd */ - ret = dcmipp_ent_sd_register(&par->ved, &par->sd, v4l2_dev, - entity_name, MEDIA_ENT_F_VID_IF_BRIDGE, - ARRAY_SIZE(pads_flag), pads_flag, - &dcmipp_par_int_ops, &dcmipp_par_ops, - NULL, NULL); - if (ret) { - kfree(par); - return ERR_PTR(ret); - } - - par->dev = dev; - - return &par->ved; -} -- cgit v1.2.3