diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-12-13 11:36:58 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-12-13 11:36:58 -0800 |
commit | cdb9d3537711939e4d8fd0de2889c966f88346eb (patch) | |
tree | 2a118e0930d3e08c6303b579e89f0df388cba4e6 /drivers/staging | |
parent | 102f9d3d455870844c47b82322c2dfc0a35eb745 (diff) | |
parent | 3178804c64ef7c8c87a53cd5bba0b2942dd64fec (diff) | |
download | linux-cdb9d3537711939e4d8fd0de2889c966f88346eb.tar.bz2 |
Merge tag 'media/v6.2-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab:
- DVB core changes to avoid refcount troubles and UAF
- DVB API/core has gained support for DVB-C2 and DVB-S2X
- New sensor drivers: ov08x40, ov4689.c, st-vgxy61 and tc358746.c
- Removal of an unused sensor driver: s5k4ecgx
- Move microchip_csi2dc to a new directory, named after the
manufacturer
- Add media controller support to Microship drivers
- Old Atmel/Microship drivers that don't use media controler got moved
to staging
- New drivers added for Renesas RZ/G2L CRU and MIPI CSI-2 support
- Allwinner A31 camera sensor driver code was now split into a bridge
and a separate processor driver
- Added a virtual stateless decoder driver in order to test core
support for stateless drivers and test userspace apps using it
- removed platform-based support for ov9650, as this is not used
anymore
- atomisp now uses videobuf2 and supports normal mmap mode
- the imx7-media-csi driver got promoted from staging
- rcar-vin driver has gained support for gen3 UDS (Up Down Scaler)
- most i2c drivers now use I2C .probe_new() kAPI
- lots of drivers fixes, cleanups and improvements
* tag 'media/v6.2-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (544 commits)
media: s5c73m3: Switch to GPIO descriptors
media: i2c: s5k5baf: switch to using gpiod API
media: i2c: s5k6a3: switch to using gpiod API
media: imx: remove code for non-existing config IMX_GPT_ICAP
media: si470x: Fix use-after-free in si470x_int_in_callback()
media: staging: stkwebcam: Restore MEDIA_{USB,CAMERA}_SUPPORT dependencies
media: coda: Add check for kmalloc
media: coda: Add check for dcoda_iram_alloc
dt-bindings: media: s5c73m3: Fix reset-gpio descriptor
media: dt-bindings: allwinner: h6-vpu-g2: Add IOMMU reference property
media: s5k4ecgx: Delete driver
media: s5k4ecgx: Switch to GPIO descriptors
media: Switch to use dev_err_probe() helper
headers: Remove some left-over license text in include/uapi/linux/v4l2-*
headers: Remove some left-over license text in include/uapi/linux/dvb/
media: usb: pwc-uncompress: Use flex array destination for memcpy()
media: s5p-mfc: Fix to handle reference queue during finishing
media: s5p-mfc: Clear workbit to handle error condition
media: s5p-mfc: Fix in register read and write for H264
media: imx: Use get_mbus_config instead of parsing upstream DT endpoints
...
Diffstat (limited to 'drivers/staging')
98 files changed, 8801 insertions, 5418 deletions
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index d4f03b203ae5..b79f93684c4f 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -51,6 +51,7 @@ menuconfig STAGING_MEDIA_DEPRECATED If in doubt, say N here. if STAGING_MEDIA_DEPRECATED +source "drivers/staging/media/deprecated/atmel/Kconfig" source "drivers/staging/media/deprecated/cpia2/Kconfig" source "drivers/staging/media/deprecated/fsl-viu/Kconfig" source "drivers/staging/media/deprecated/meye/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index a387692b84f2..54bbdd4b0d08 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_VIDEO_ATMEL_ISC_BASE) += deprecated/atmel/ obj-$(CONFIG_INTEL_ATOMISP) += atomisp/ obj-$(CONFIG_VIDEO_CPIA2) += deprecated/cpia2/ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/ diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index 783f1b88ebf2..87a634bf9ff5 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -786,8 +786,6 @@ static int gpio_ctrl(struct v4l2_subdev *sd, bool flag) return ret; } -static int power_down(struct v4l2_subdev *sd); - static int power_up(struct v4l2_subdev *sd) { struct gc0310_device *dev = to_gc0310_sensor(sd); @@ -800,6 +798,9 @@ static int power_up(struct v4l2_subdev *sd) return -ENODEV; } + if (dev->power_on) + return 0; /* Already on */ + /* power control */ ret = power_ctrl(sd, 1); if (ret) @@ -820,6 +821,7 @@ static int power_up(struct v4l2_subdev *sd) msleep(100); + dev->power_on = true; return 0; fail_gpio: @@ -844,6 +846,9 @@ static int power_down(struct v4l2_subdev *sd) return -ENODEV; } + if (!dev->power_on) + return 0; /* Already off */ + /* gpio ctrl */ ret = gpio_ctrl(sd, 0); if (ret) { @@ -861,6 +866,7 @@ static int power_down(struct v4l2_subdev *sd) if (ret) dev_err(&client->dev, "vprog failed.\n"); + dev->power_on = false; return ret; } @@ -935,6 +941,9 @@ static int gc0310_set_fmt(struct v4l2_subdev *sd, return 0; } + /* s_power has not been called yet for std v4l2 clients (camorama) */ + power_up(sd); + dev_dbg(&client->dev, "%s: before gc0310_write_reg_array %s\n", __func__, dev->res->desc); ret = startup(sd); @@ -1073,6 +1082,7 @@ static int gc0310_s_config(struct v4l2_subdev *sd, * as first power on by board may not fulfill the * power on sequqence needed by the module */ + dev->power_on = true; /* force power_down() to run */ ret = power_down(sd); if (ret) { dev_err(&client->dev, "gc0310 power-off err.\n"); diff --git a/drivers/staging/media/atomisp/i2c/gc0310.h b/drivers/staging/media/atomisp/i2c/gc0310.h index db643ebc3909..4b9ce681bd93 100644 --- a/drivers/staging/media/atomisp/i2c/gc0310.h +++ b/drivers/staging/media/atomisp/i2c/gc0310.h @@ -152,6 +152,7 @@ struct gc0310_device { int vt_pix_clk_freq_mhz; struct gc0310_resolution *res; u8 type; + bool power_on; }; enum gc0310_tok_type { diff --git a/drivers/staging/media/atomisp/i2c/ov2680.h b/drivers/staging/media/atomisp/i2c/ov2680.h index 4e351196fe34..7ab337b859ad 100644 --- a/drivers/staging/media/atomisp/i2c/ov2680.h +++ b/drivers/staging/media/atomisp/i2c/ov2680.h @@ -485,19 +485,19 @@ static struct ov2680_reg const ov2680_720x592_30fps[] = { static struct ov2680_reg const ov2680_800x600_30fps[] = { {0x3086, 0x01}, {0x370a, 0x23}, - {0x3801, 0x00}, + {0x3801, 0x00}, /* hstart 0 */ {0x3802, 0x00}, - {0x3803, 0x00}, + {0x3803, 0x00}, /* vstart 0 */ {0x3804, 0x06}, - {0x3805, 0x4f}, + {0x3805, 0x4f}, /* hend 1615 */ {0x3806, 0x04}, - {0x3807, 0xbf}, + {0x3807, 0xbf}, /* vend 1215 */ {0x3808, 0x03}, - {0x3809, 0x20}, + {0x3809, 0x20}, /* hsize 800 */ {0x380a, 0x02}, - {0x380b, 0x58}, + {0x380b, 0x58}, /* vsize 600 */ {0x380c, 0x06}, - {0x380d, 0xac}, + {0x380d, 0xac}, /* htotal 1708 */ {0x3810, 0x00}, {0x3811, 0x00}, {0x3812, 0x00}, @@ -524,19 +524,19 @@ static struct ov2680_reg const ov2680_800x600_30fps[] = { static struct ov2680_reg const ov2680_720p_30fps[] = { {0x3086, 0x00}, {0x370a, 0x21}, - {0x3801, 0xa0}, + {0x3801, 0xa0}, /* hstart 160 */ {0x3802, 0x00}, - {0x3803, 0xf2}, + {0x3803, 0xf2}, /* vstart 242 */ {0x3804, 0x05}, - {0x3805, 0xbf}, + {0x3805, 0xbf}, /* hend 1471 */ {0x3806, 0x03}, - {0x3807, 0xdd}, + {0x3807, 0xdd}, /* vend 989 */ {0x3808, 0x05}, - {0x3809, 0x10}, + {0x3809, 0x10}, /* hsize 1296 */ {0x380a, 0x02}, - {0x380b, 0xe0}, + {0x380b, 0xe0}, /* vsize 736 */ {0x380c, 0x06}, - {0x380d, 0xa8}, + {0x380d, 0xa8}, /* htotal 1704 */ {0x3810, 0x00}, {0x3811, 0x08}, {0x3812, 0x00}, @@ -563,19 +563,19 @@ static struct ov2680_reg const ov2680_720p_30fps[] = { static struct ov2680_reg const ov2680_1296x976_30fps[] = { {0x3086, 0x00}, {0x370a, 0x21}, - {0x3801, 0xa0}, + {0x3801, 0xa0}, /* hstart 160 */ {0x3802, 0x00}, - {0x3803, 0x78}, + {0x3803, 0x78}, /* vstart 120 */ {0x3804, 0x05}, - {0x3805, 0xbf}, + {0x3805, 0xbf}, /* hend 1471 */ {0x3806, 0x04}, - {0x3807, 0x57}, + {0x3807, 0x57}, /* vend 1111 */ {0x3808, 0x05}, - {0x3809, 0x10}, + {0x3809, 0x10}, /* hsize 1296 */ {0x380a, 0x03}, - {0x380b, 0xd0}, + {0x380b, 0xd0}, /* vsize 976 */ {0x380c, 0x06}, - {0x380d, 0xa8}, + {0x380d, 0xa8}, /* htotal 1704 */ {0x3810, 0x00}, {0x3811, 0x08}, {0x3812, 0x00}, @@ -820,8 +820,8 @@ static struct ov2680_resolution ov2680_res_preview[] = { .regs = ov2680_1296x976_30fps, }, { - .width = 1280, - .height = 720, + .width = 1296, + .height = 736, .fps = 60, .pix_clk_freq = 66, .pixels_per_line = 1698,//1704, diff --git a/drivers/staging/media/atomisp/include/hmm/hmm.h b/drivers/staging/media/atomisp/include/hmm/hmm.h index c0384bb0a762..2bc323b34f89 100644 --- a/drivers/staging/media/atomisp/include/hmm/hmm.h +++ b/drivers/staging/media/atomisp/include/hmm/hmm.h @@ -37,7 +37,8 @@ int hmm_init(void); void hmm_cleanup(void); ia_css_ptr hmm_alloc(size_t bytes); -ia_css_ptr hmm_create_from_userdata(size_t bytes, const void __user *userptr); +ia_css_ptr hmm_create_from_vmalloc_buf(size_t bytes, void *vmalloc_addr); + void hmm_free(ia_css_ptr ptr); int hmm_load(ia_css_ptr virt, void *data, unsigned int bytes); int hmm_store(ia_css_ptr virt, const void *data, unsigned int bytes); diff --git a/drivers/staging/media/atomisp/include/hmm/hmm_bo.h b/drivers/staging/media/atomisp/include/hmm/hmm_bo.h index c5cbae1d9cf9..b4c03e0ca9c0 100644 --- a/drivers/staging/media/atomisp/include/hmm/hmm_bo.h +++ b/drivers/staging/media/atomisp/include/hmm/hmm_bo.h @@ -73,7 +73,7 @@ enum hmm_bo_type { HMM_BO_PRIVATE, - HMM_BO_USER, + HMM_BO_VMALLOC, HMM_BO_LAST, }; @@ -207,7 +207,7 @@ int hmm_bo_allocated(struct hmm_buffer_object *bo); */ int hmm_bo_alloc_pages(struct hmm_buffer_object *bo, enum hmm_bo_type type, - const void __user *userptr); + void *vmalloc_addr); void hmm_bo_free_pages(struct hmm_buffer_object *bo); int hmm_bo_page_allocated(struct hmm_buffer_object *bo); diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index c72d0e344671..d8c7e7367386 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -30,7 +30,6 @@ #include <asm/iosf_mbi.h> #include <media/v4l2-event.h> -#include <media/videobuf-vmalloc.h> #define CREATE_TRACE_POINTS #include "atomisp_trace_event.h" @@ -208,11 +207,6 @@ int atomisp_freq_scaling(struct atomisp_device *isp, int i, ret; unsigned short fps = 0; - if (isp->sw_contex.power_state != ATOM_ISP_POWER_UP) { - dev_err(isp->dev, "DFS cannot proceed due to no power.\n"); - return -EINVAL; - } - if ((pdev->device & ATOMISP_PCI_DEVICE_SOC_MASK) == ATOMISP_PCI_DEVICE_SOC_CHT && ATOMISP_USE_YUVPP(asd)) isp->dfs = &dfs_config_cht_soc; @@ -308,24 +302,16 @@ int atomisp_reset(struct atomisp_device *isp) int ret = 0; dev_dbg(isp->dev, "%s\n", __func__); - atomisp_css_suspend(isp); - ret = atomisp_runtime_suspend(isp->dev); + + ret = atomisp_power_off(isp->dev); if (ret < 0) - dev_err(isp->dev, "atomisp_runtime_suspend failed, %d\n", ret); - ret = atomisp_mrfld_power_down(isp); + dev_err(isp->dev, "atomisp_power_off failed, %d\n", ret); + + ret = atomisp_power_on(isp->dev); if (ret < 0) { - dev_err(isp->dev, "can not disable ISP power\n"); - } else { - ret = atomisp_mrfld_power_up(isp); - if (ret < 0) - dev_err(isp->dev, "can not enable ISP power\n"); - ret = atomisp_runtime_resume(isp->dev); - if (ret < 0) - dev_err(isp->dev, "atomisp_runtime_resume failed, %d\n", ret); - } - ret = atomisp_css_resume(isp); - if (ret) + dev_err(isp->dev, "atomisp_power_on failed, %d\n", ret); isp->isp_fatal_error = true; + } return ret; } @@ -518,8 +504,8 @@ irqreturn_t atomisp_isr(int irq, void *dev) int err; spin_lock_irqsave(&isp->lock, flags); - if (isp->sw_contex.power_state != ATOM_ISP_POWER_UP || - !isp->css_initialized) { + + if (!isp->css_initialized) { spin_unlock_irqrestore(&isp->lock, flags); return IRQ_HANDLED; } @@ -634,25 +620,6 @@ void atomisp_clear_css_buffer_counters(struct atomisp_sub_device *asd) memset(asd->metadata_bufs_in_css[i], 0, sizeof(asd->metadata_bufs_in_css[i])); asd->dis_bufs_in_css = 0; - asd->video_out_capture.buffers_in_css = 0; - asd->video_out_vf.buffers_in_css = 0; - asd->video_out_preview.buffers_in_css = 0; - asd->video_out_video_capture.buffers_in_css = 0; -} - -/* ISP2400 */ -bool atomisp_buffers_queued(struct atomisp_sub_device *asd) -{ - return asd->video_out_capture.buffers_in_css || - asd->video_out_vf.buffers_in_css || - asd->video_out_preview.buffers_in_css || - asd->video_out_video_capture.buffers_in_css; -} - -/* ISP2401 */ -bool atomisp_buffers_queued_pipe(struct atomisp_video_pipe *pipe) -{ - return pipe->buffers_in_css ? true : false; } /* 0x100000 is the start of dmem inside SP */ @@ -681,57 +648,68 @@ void dump_sp_dmem(struct atomisp_device *isp, unsigned int addr, } while (--size32); } -static struct videobuf_buffer *atomisp_css_frame_to_vbuf( - struct atomisp_video_pipe *pipe, struct ia_css_frame *frame) +int atomisp_buffers_in_css(struct atomisp_video_pipe *pipe) { - struct videobuf_vmalloc_memory *vm_mem; - struct ia_css_frame *handle; - int i; + unsigned long irqflags; + struct list_head *pos; + int buffers_in_css = 0; - for (i = 0; pipe->capq.bufs[i]; i++) { - vm_mem = pipe->capq.bufs[i]->priv; - handle = vm_mem->vaddr; - if (handle && handle->data == frame->data) - return pipe->capq.bufs[i]; - } + spin_lock_irqsave(&pipe->irq_lock, irqflags); - return NULL; + list_for_each(pos, &pipe->buffers_in_css) + buffers_in_css++; + + spin_unlock_irqrestore(&pipe->irq_lock, irqflags); + + return buffers_in_css; } -static void atomisp_flush_video_pipe(struct atomisp_sub_device *asd, - struct atomisp_video_pipe *pipe) +void atomisp_buffer_done(struct ia_css_frame *frame, enum vb2_buffer_state state) { + struct atomisp_video_pipe *pipe = vb_to_pipe(&frame->vb.vb2_buf); + + lockdep_assert_held(&pipe->irq_lock); + + frame->vb.vb2_buf.timestamp = ktime_get_ns(); + frame->vb.field = pipe->pix.field; + frame->vb.sequence = atomic_read(&pipe->asd->sequence); + list_del(&frame->queue); + if (state == VB2_BUF_STATE_DONE) + vb2_set_plane_payload(&frame->vb.vb2_buf, 0, pipe->pix.sizeimage); + vb2_buffer_done(&frame->vb.vb2_buf, state); +} + +void atomisp_flush_video_pipe(struct atomisp_video_pipe *pipe, bool warn_on_css_frames) +{ + struct ia_css_frame *frame, *_frame; unsigned long irqflags; - int i; - if (!pipe->users) - return; + spin_lock_irqsave(&pipe->irq_lock, irqflags); - for (i = 0; pipe->capq.bufs[i]; i++) { - spin_lock_irqsave(&pipe->irq_lock, irqflags); - if (pipe->capq.bufs[i]->state == VIDEOBUF_ACTIVE || - pipe->capq.bufs[i]->state == VIDEOBUF_QUEUED) { - pipe->capq.bufs[i]->ts = ktime_get_ns(); - pipe->capq.bufs[i]->field_count = - atomic_read(&asd->sequence) << 1; - dev_dbg(asd->isp->dev, "release buffers on device %s\n", - pipe->vdev.name); - if (pipe->capq.bufs[i]->state == VIDEOBUF_QUEUED) - list_del_init(&pipe->capq.bufs[i]->queue); - pipe->capq.bufs[i]->state = VIDEOBUF_ERROR; - wake_up(&pipe->capq.bufs[i]->done); - } - spin_unlock_irqrestore(&pipe->irq_lock, irqflags); + list_for_each_entry_safe(frame, _frame, &pipe->buffers_in_css, queue) { + if (warn_on_css_frames) + dev_warn(pipe->isp->dev, "Warning: CSS frames queued on flush\n"); + atomisp_buffer_done(frame, VB2_BUF_STATE_ERROR); + } + + list_for_each_entry_safe(frame, _frame, &pipe->activeq, queue) + atomisp_buffer_done(frame, VB2_BUF_STATE_ERROR); + + list_for_each_entry_safe(frame, _frame, &pipe->buffers_waiting_for_param, queue) { + pipe->frame_request_config_id[frame->vb.vb2_buf.index] = 0; + atomisp_buffer_done(frame, VB2_BUF_STATE_ERROR); } + + spin_unlock_irqrestore(&pipe->irq_lock, irqflags); } /* Returns queued buffers back to video-core */ void atomisp_flush_bufs_and_wakeup(struct atomisp_sub_device *asd) { - atomisp_flush_video_pipe(asd, &asd->video_out_capture); - atomisp_flush_video_pipe(asd, &asd->video_out_vf); - atomisp_flush_video_pipe(asd, &asd->video_out_preview); - atomisp_flush_video_pipe(asd, &asd->video_out_video_capture); + atomisp_flush_video_pipe(&asd->video_out_capture, false); + atomisp_flush_video_pipe(&asd->video_out_vf, false); + atomisp_flush_video_pipe(&asd->video_out_preview, false); + atomisp_flush_video_pipe(&asd->video_out_video_capture, false); } /* clean out the parameters that did not apply */ @@ -763,93 +741,6 @@ static void atomisp_recover_params_queue(struct atomisp_video_pipe *pipe) atomisp_handle_parameter_and_buffer(pipe); } -/* find atomisp_video_pipe with css pipe id, buffer type and atomisp run_mode */ -static struct atomisp_video_pipe *__atomisp_get_pipe( - struct atomisp_sub_device *asd, - enum atomisp_input_stream_id stream_id, - enum ia_css_pipe_id css_pipe_id, - enum ia_css_buffer_type buf_type) -{ - /* video is same in online as in continuouscapture mode */ - if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_LOWLAT) { - /* - * Disable vf_pp and run CSS in still capture mode. In this - * mode, CSS does not cause extra latency with buffering, but - * scaling is not available. - */ - return &asd->video_out_capture; - } else if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_SCALER) { - /* - * Disable vf_pp and run CSS in video mode. This allows using - * ISP scaling but it has one frame delay due to CSS internal - * buffering. - */ - return &asd->video_out_video_capture; - } else if (css_pipe_id == IA_CSS_PIPE_ID_YUVPP) { - /* - * to SOC camera, yuvpp pipe is run for capture/video/SDV/ZSL. - */ - if (asd->continuous_mode->val) { - if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) { - /* SDV case */ - switch (buf_type) { - case IA_CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME: - return &asd->video_out_video_capture; - case IA_CSS_BUFFER_TYPE_SEC_VF_OUTPUT_FRAME: - return &asd->video_out_preview; - case IA_CSS_BUFFER_TYPE_OUTPUT_FRAME: - return &asd->video_out_capture; - default: - return &asd->video_out_vf; - } - } else if (asd->run_mode->val == ATOMISP_RUN_MODE_PREVIEW) { - /* ZSL case */ - switch (buf_type) { - case IA_CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME: - return &asd->video_out_preview; - case IA_CSS_BUFFER_TYPE_OUTPUT_FRAME: - return &asd->video_out_capture; - default: - return &asd->video_out_vf; - } - } - } else if (buf_type == IA_CSS_BUFFER_TYPE_OUTPUT_FRAME) { - switch (asd->run_mode->val) { - case ATOMISP_RUN_MODE_VIDEO: - return &asd->video_out_video_capture; - case ATOMISP_RUN_MODE_PREVIEW: - return &asd->video_out_preview; - default: - return &asd->video_out_capture; - } - } else if (buf_type == IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME) { - if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) - return &asd->video_out_preview; - else - return &asd->video_out_vf; - } - } else if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) { - /* For online video or SDV video pipe. */ - if (css_pipe_id == IA_CSS_PIPE_ID_VIDEO || - css_pipe_id == IA_CSS_PIPE_ID_COPY || - css_pipe_id == IA_CSS_PIPE_ID_YUVPP) { - if (buf_type == IA_CSS_BUFFER_TYPE_OUTPUT_FRAME) - return &asd->video_out_video_capture; - return &asd->video_out_preview; - } - } else if (asd->run_mode->val == ATOMISP_RUN_MODE_PREVIEW) { - /* For online preview or ZSL preview pipe. */ - if (css_pipe_id == IA_CSS_PIPE_ID_PREVIEW || - css_pipe_id == IA_CSS_PIPE_ID_COPY || - css_pipe_id == IA_CSS_PIPE_ID_YUVPP) - return &asd->video_out_preview; - } - /* For capture pipe. */ - if (buf_type == IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME) - return &asd->video_out_vf; - return &asd->video_out_capture; -} - enum atomisp_metadata_type atomisp_get_metadata_type(struct atomisp_sub_device *asd, enum ia_css_pipe_id pipe_id) @@ -868,11 +759,9 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, enum ia_css_pipe_id css_pipe_id, bool q_buffers, enum atomisp_input_stream_id stream_id) { - struct videobuf_buffer *vb = NULL; struct atomisp_video_pipe *pipe = NULL; struct atomisp_css_buffer buffer; bool requeue = false; - int err; unsigned long irqflags; struct ia_css_frame *frame = NULL; struct atomisp_s3a_buf *s3a_buf = NULL, *_s3a_buf_tmp, *s3a_iter; @@ -881,6 +770,7 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, enum atomisp_metadata_type md_type; struct atomisp_device *isp = asd->isp; struct v4l2_control ctrl; + int i, err; lockdep_assert_held(&isp->mutex); @@ -908,13 +798,6 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, return; } - /* need to know the atomisp pipe for frame buffers */ - pipe = __atomisp_get_pipe(asd, stream_id, css_pipe_id, buf_type); - if (!pipe) { - dev_err(isp->dev, "error getting atomisp pipe\n"); - return; - } - switch (buf_type) { case IA_CSS_BUFFER_TYPE_3A_STATISTICS: list_for_each_entry_safe(s3a_iter, _s3a_buf_tmp, @@ -989,7 +872,6 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, break; case IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME: case IA_CSS_BUFFER_TYPE_SEC_VF_OUTPUT_FRAME: - pipe->buffers_in_css--; frame = buffer.css_buffer.data.frame; if (!frame) { WARN_ON(1); @@ -998,6 +880,8 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, if (!frame->valid) error = true; + pipe = vb_to_pipe(&frame->vb.vb2_buf); + /* FIXME: * YUVPP doesn't set postview exp_id correctlly in SDV mode. * This is a WORKAROUND to set exp_id. see HSDES-1503911606. @@ -1022,10 +906,7 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, dev_dbg(isp->dev, "%s thumb no flash in this frame\n", __func__); } - vb = atomisp_css_frame_to_vbuf(pipe, frame); - WARN_ON(!vb); - if (vb) - pipe->frame_config_id[vb->i] = frame->isp_config_id; + pipe->frame_config_id[frame->vb.vb2_buf.index] = frame->isp_config_id; if (css_pipe_id == IA_CSS_PIPE_ID_CAPTURE && asd->pending_capture_request > 0) { err = atomisp_css_offline_capture_configure(asd, @@ -1041,7 +922,6 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, break; case IA_CSS_BUFFER_TYPE_OUTPUT_FRAME: case IA_CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME: - pipe->buffers_in_css--; frame = buffer.css_buffer.data.frame; if (!frame) { WARN_ON(1); @@ -1051,6 +931,8 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, if (!frame->valid) error = true; + pipe = vb_to_pipe(&frame->vb.vb2_buf); + /* FIXME: * YUVPP doesn't set preview exp_id correctlly in ZSL mode. * This is a WORKAROUND to set exp_id. see HSDES-1503911606. @@ -1062,72 +944,53 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, dev_dbg(isp->dev, "%s: main frame with exp_id %d is ready\n", __func__, frame->exp_id); - vb = atomisp_css_frame_to_vbuf(pipe, frame); - if (!vb) { - WARN_ON(1); - break; - } + + i = frame->vb.vb2_buf.index; /* free the parameters */ - if (pipe->frame_params[vb->i]) { - if (asd->params.dvs_6axis == - pipe->frame_params[vb->i]->params.dvs_6axis) + if (pipe->frame_params[i]) { + if (asd->params.dvs_6axis == pipe->frame_params[i]->params.dvs_6axis) asd->params.dvs_6axis = NULL; - atomisp_free_css_parameters( - &pipe->frame_params[vb->i]->params); - kvfree(pipe->frame_params[vb->i]); - pipe->frame_params[vb->i] = NULL; + atomisp_free_css_parameters(&pipe->frame_params[i]->params); + kvfree(pipe->frame_params[i]); + pipe->frame_params[i] = NULL; } - pipe->frame_config_id[vb->i] = frame->isp_config_id; + pipe->frame_config_id[i] = frame->isp_config_id; ctrl.id = V4L2_CID_FLASH_MODE; if (asd->params.flash_state == ATOMISP_FLASH_ONGOING) { - if (frame->flash_state - == IA_CSS_FRAME_FLASH_STATE_PARTIAL) { - asd->frame_status[vb->i] = - ATOMISP_FRAME_STATUS_FLASH_PARTIAL; - dev_dbg(isp->dev, "%s partially flashed\n", - __func__); - } else if (frame->flash_state - == IA_CSS_FRAME_FLASH_STATE_FULL) { - asd->frame_status[vb->i] = - ATOMISP_FRAME_STATUS_FLASH_EXPOSED; + if (frame->flash_state == IA_CSS_FRAME_FLASH_STATE_PARTIAL) { + asd->frame_status[i] = ATOMISP_FRAME_STATUS_FLASH_PARTIAL; + dev_dbg(isp->dev, "%s partially flashed\n", __func__); + } else if (frame->flash_state == IA_CSS_FRAME_FLASH_STATE_FULL) { + asd->frame_status[i] = ATOMISP_FRAME_STATUS_FLASH_EXPOSED; asd->params.num_flash_frames--; - dev_dbg(isp->dev, "%s completely flashed\n", - __func__); + dev_dbg(isp->dev, "%s completely flashed\n", __func__); } else { - asd->frame_status[vb->i] = - ATOMISP_FRAME_STATUS_OK; - dev_dbg(isp->dev, - "%s no flash in this frame\n", - __func__); + asd->frame_status[i] = ATOMISP_FRAME_STATUS_OK; + dev_dbg(isp->dev, "%s no flash in this frame\n", __func__); } /* Check if flashing sequence is done */ - if (asd->frame_status[vb->i] == - ATOMISP_FRAME_STATUS_FLASH_EXPOSED) + if (asd->frame_status[i] == ATOMISP_FRAME_STATUS_FLASH_EXPOSED) asd->params.flash_state = ATOMISP_FLASH_DONE; } else if (isp->flash) { - if (v4l2_g_ctrl(isp->flash->ctrl_handler, &ctrl) == - 0 && ctrl.value == ATOMISP_FLASH_MODE_TORCH) { + if (v4l2_g_ctrl(isp->flash->ctrl_handler, &ctrl) == 0 && + ctrl.value == ATOMISP_FLASH_MODE_TORCH) { ctrl.id = V4L2_CID_FLASH_TORCH_INTENSITY; - if (v4l2_g_ctrl(isp->flash->ctrl_handler, &ctrl) - == 0 && ctrl.value > 0) { - asd->frame_status[vb->i] = - ATOMISP_FRAME_STATUS_FLASH_EXPOSED; - } else { - asd->frame_status[vb->i] = - ATOMISP_FRAME_STATUS_OK; - } + if (v4l2_g_ctrl(isp->flash->ctrl_handler, &ctrl) == 0 && + ctrl.value > 0) + asd->frame_status[i] = ATOMISP_FRAME_STATUS_FLASH_EXPOSED; + else + asd->frame_status[i] = ATOMISP_FRAME_STATUS_OK; } else { - asd->frame_status[vb->i] = - ATOMISP_FRAME_STATUS_OK; + asd->frame_status[i] = ATOMISP_FRAME_STATUS_OK; } } else { - asd->frame_status[vb->i] = ATOMISP_FRAME_STATUS_OK; + asd->frame_status[i] = ATOMISP_FRAME_STATUS_OK; } - asd->params.last_frame_status = asd->frame_status[vb->i]; + asd->params.last_frame_status = asd->frame_status[i]; if (asd->continuous_mode->val) { if (css_pipe_id == IA_CSS_PIPE_ID_PREVIEW || @@ -1193,20 +1056,10 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, default: break; } - if (vb) { - vb->ts = ktime_get_ns(); - vb->field_count = atomic_read(&asd->sequence) << 1; - /*mark videobuffer done for dequeue*/ + if (frame) { spin_lock_irqsave(&pipe->irq_lock, irqflags); - vb->state = !error ? VIDEOBUF_DONE : VIDEOBUF_ERROR; + atomisp_buffer_done(frame, error ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); spin_unlock_irqrestore(&pipe->irq_lock, irqflags); - - /* - * Frame capture done, wake up any process block on - * current active buffer - * possibly hold by videobuf_dqbuf() - */ - wake_up(&vb->done); } /* @@ -3288,7 +3141,7 @@ int atomisp_css_cp_dvs2_coefs(struct atomisp_sub_device *asd, if (!IS_ISP2401) { if (sizeof(*cur) != sizeof(coefs->grid) || memcmp(&coefs->grid, cur, sizeof(coefs->grid))) { - dev_err(asd->isp->dev, "dvs grid mis-match!\n"); + dev_err(asd->isp->dev, "dvs grid mismatch!\n"); /* If the grid info in the argument differs from the current grid info, we tell the caller to reset the grid size and try again. */ @@ -3344,7 +3197,7 @@ int atomisp_css_cp_dvs2_coefs(struct atomisp_sub_device *asd, if (sizeof(*cur) != sizeof(dvs2_coefs.grid) || memcmp(&dvs2_coefs.grid, cur, sizeof(dvs2_coefs.grid))) { - dev_err(asd->isp->dev, "dvs grid mis-match!\n"); + dev_err(asd->isp->dev, "dvs grid mismatch!\n"); /* If the grid info in the argument differs from the current grid info, we tell the caller to reset the grid size and try again. */ @@ -3676,6 +3529,18 @@ void atomisp_free_css_parameters(struct atomisp_css_params *css_param) } } +static void atomisp_move_frame_to_activeq(struct ia_css_frame *frame, + struct atomisp_css_params_with_list *param) +{ + struct atomisp_video_pipe *pipe = vb_to_pipe(&frame->vb.vb2_buf); + unsigned long irqflags; + + pipe->frame_params[frame->vb.vb2_buf.index] = param; + spin_lock_irqsave(&pipe->irq_lock, irqflags); + list_move_tail(&frame->queue, &pipe->activeq); + spin_unlock_irqrestore(&pipe->irq_lock, irqflags); +} + /* * Check parameter queue list and buffer queue list to find out if matched items * and then set parameter to CSS and enqueue buffer to CSS. @@ -3686,13 +3551,10 @@ void atomisp_free_css_parameters(struct atomisp_css_params *css_param) void atomisp_handle_parameter_and_buffer(struct atomisp_video_pipe *pipe) { struct atomisp_sub_device *asd = pipe->asd; - struct videobuf_buffer *vb = NULL, *vb_tmp; + struct ia_css_frame *frame = NULL, *frame_tmp; struct atomisp_css_params_with_list *param = NULL, *param_tmp; - struct videobuf_vmalloc_memory *vm_mem = NULL; - unsigned long irqflags; bool need_to_enqueue_buffer = false; - - lockdep_assert_held(&asd->isp->mutex); + int i; if (!asd) { dev_err(pipe->isp->dev, "%s(): asd is NULL, device is %s\n", @@ -3700,6 +3562,8 @@ void atomisp_handle_parameter_and_buffer(struct atomisp_video_pipe *pipe) return; } + lockdep_assert_held(&asd->isp->mutex); + if (atomisp_is_vf_pipe(pipe)) return; @@ -3714,44 +3578,32 @@ void atomisp_handle_parameter_and_buffer(struct atomisp_video_pipe *pipe) list_empty(&pipe->buffers_waiting_for_param)) return; - list_for_each_entry_safe(vb, vb_tmp, + list_for_each_entry_safe(frame, frame_tmp, &pipe->buffers_waiting_for_param, queue) { - if (pipe->frame_request_config_id[vb->i]) { + i = frame->vb.vb2_buf.index; + if (pipe->frame_request_config_id[i]) { list_for_each_entry_safe(param, param_tmp, &pipe->per_frame_params, list) { - if (pipe->frame_request_config_id[vb->i] != - param->params.isp_config_id) + if (pipe->frame_request_config_id[i] != param->params.isp_config_id) continue; list_del(¶m->list); - list_del(&vb->queue); + /* * clear the request config id as the buffer * will be handled and enqueued into CSS soon */ - pipe->frame_request_config_id[vb->i] = 0; - pipe->frame_params[vb->i] = param; - vm_mem = vb->priv; - BUG_ON(!vm_mem); + pipe->frame_request_config_id[i] = 0; + atomisp_move_frame_to_activeq(frame, param); + need_to_enqueue_buffer = true; break; } - if (vm_mem) { - spin_lock_irqsave(&pipe->irq_lock, irqflags); - list_add_tail(&vb->queue, &pipe->activeq); - spin_unlock_irqrestore(&pipe->irq_lock, irqflags); - vm_mem = NULL; - need_to_enqueue_buffer = true; - } else { - /* The is the end, stop further loop */ + /* If this is the end, stop further loop */ + if (list_entry_is_head(param, &pipe->per_frame_params, list)) break; - } } else { - list_del(&vb->queue); - pipe->frame_params[vb->i] = NULL; - spin_lock_irqsave(&pipe->irq_lock, irqflags); - list_add_tail(&vb->queue, &pipe->activeq); - spin_unlock_irqrestore(&pipe->irq_lock, irqflags); + atomisp_move_frame_to_activeq(frame, NULL); need_to_enqueue_buffer = true; } } @@ -3774,14 +3626,14 @@ int atomisp_set_parameters(struct video_device *vdev, struct atomisp_css_params *css_param = &asd->params.css_param; int ret; - lockdep_assert_held(&asd->isp->mutex); - if (!asd) { dev_err(pipe->isp->dev, "%s(): asd is NULL, device is %s\n", __func__, vdev->name); return -EINVAL; } + lockdep_assert_held(&asd->isp->mutex); + if (!asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream) { dev_err(asd->isp->dev, "%s: internal error!\n", __func__); return -EINVAL; @@ -5140,9 +4992,8 @@ static int atomisp_set_fmt_to_snr(struct video_device *vdev, return css_input_resolution_changed(asd, ffmt); } -int atomisp_set_fmt(struct file *file, void *unused, struct v4l2_format *f) +int atomisp_set_fmt(struct video_device *vdev, struct v4l2_format *f) { - struct video_device *vdev = video_devdata(file); struct atomisp_device *isp = video_get_drvdata(vdev); struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); struct atomisp_sub_device *asd = pipe->asd; @@ -5558,8 +5409,6 @@ done: f->fmt.pix.priv = PAGE_ALIGN(pipe->pix.width * pipe->pix.height * 2); - pipe->capq.field = f->fmt.pix.field; - /* * If in video 480P case, no GFX throttle */ @@ -5643,51 +5492,6 @@ out: return ret; } -/*Turn off ISP dphy */ -int atomisp_ospm_dphy_down(struct atomisp_device *isp) -{ - struct pci_dev *pdev = to_pci_dev(isp->dev); - unsigned long flags; - u32 reg; - - dev_dbg(isp->dev, "%s\n", __func__); - - /* if ISP timeout, we can force powerdown */ - if (isp->isp_timeout) - goto done; - - if (!atomisp_dev_users(isp)) - goto done; - - spin_lock_irqsave(&isp->lock, flags); - isp->sw_contex.power_state = ATOM_ISP_POWER_DOWN; - spin_unlock_irqrestore(&isp->lock, flags); -done: - /* - * MRFLD IUNIT DPHY is located in an always-power-on island - * MRFLD HW design need all CSI ports are disabled before - * powering down the IUNIT. - */ - pci_read_config_dword(pdev, MRFLD_PCI_CSI_CONTROL, ®); - reg |= MRFLD_ALL_CSI_PORTS_OFF_MASK; - pci_write_config_dword(pdev, MRFLD_PCI_CSI_CONTROL, reg); - return 0; -} - -/*Turn on ISP dphy */ -int atomisp_ospm_dphy_up(struct atomisp_device *isp) -{ - unsigned long flags; - - dev_dbg(isp->dev, "%s\n", __func__); - - spin_lock_irqsave(&isp->lock, flags); - isp->sw_contex.power_state = ATOM_ISP_POWER_UP; - spin_unlock_irqrestore(&isp->lock, flags); - - return 0; -} - int atomisp_exif_makernote(struct atomisp_sub_device *asd, struct atomisp_makernote_info *config) { diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.h b/drivers/staging/media/atomisp/pci/atomisp_cmd.h index c9f92f1326b6..b8911491581a 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.h +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.h @@ -55,12 +55,11 @@ void dump_sp_dmem(struct atomisp_device *isp, unsigned int addr, struct camera_mipi_info *atomisp_to_sensor_mipi_info(struct v4l2_subdev *sd); struct atomisp_video_pipe *atomisp_to_video_pipe(struct video_device *dev); int atomisp_reset(struct atomisp_device *isp); +int atomisp_buffers_in_css(struct atomisp_video_pipe *pipe); +void atomisp_buffer_done(struct ia_css_frame *frame, enum vb2_buffer_state state); +void atomisp_flush_video_pipe(struct atomisp_video_pipe *pipe, bool warn_on_css_frames); void atomisp_flush_bufs_and_wakeup(struct atomisp_sub_device *asd); void atomisp_clear_css_buffer_counters(struct atomisp_sub_device *asd); -/* ISP2400 */ -bool atomisp_buffers_queued(struct atomisp_sub_device *asd); -/* ISP2401 */ -bool atomisp_buffers_queued_pipe(struct atomisp_video_pipe *pipe); /* Interrupt functions */ void atomisp_msi_irq_init(struct atomisp_device *isp); @@ -266,7 +265,7 @@ int atomisp_get_sensor_mode_data(struct atomisp_sub_device *asd, int atomisp_try_fmt(struct video_device *vdev, struct v4l2_pix_format *f, bool *res_overflow); -int atomisp_set_fmt(struct file *file, void *fh, struct v4l2_format *f); +int atomisp_set_fmt(struct video_device *vdev, struct v4l2_format *f); int atomisp_set_shading_table(struct atomisp_sub_device *asd, struct atomisp_shading_table *shading_table); @@ -274,8 +273,6 @@ int atomisp_set_shading_table(struct atomisp_sub_device *asd, int atomisp_offline_capture_configure(struct atomisp_sub_device *asd, struct atomisp_cont_capture_conf *cvf_config); -int atomisp_ospm_dphy_down(struct atomisp_device *isp); -int atomisp_ospm_dphy_up(struct atomisp_device *isp); int atomisp_exif_makernote(struct atomisp_sub_device *asd, struct atomisp_makernote_info *config); @@ -342,8 +339,6 @@ int atomisp_inject_a_fake_event(struct atomisp_sub_device *asd, int *event); int atomisp_get_invalid_frame_num(struct video_device *vdev, int *invalid_frame_num); -int atomisp_mrfld_power_up(struct atomisp_device *isp); -int atomisp_mrfld_power_down(struct atomisp_device *isp); -int atomisp_runtime_suspend(struct device *dev); -int atomisp_runtime_resume(struct device *dev); +int atomisp_power_off(struct device *dev); +int atomisp_power_on(struct device *dev); #endif /* __ATOMISP_CMD_H__ */ diff --git a/drivers/staging/media/atomisp/pci/atomisp_common.h b/drivers/staging/media/atomisp/pci/atomisp_common.h index b29874f2bc0f..07c38e487a66 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_common.h +++ b/drivers/staging/media/atomisp/pci/atomisp_common.h @@ -25,7 +25,7 @@ #include <linux/v4l2-mediabus.h> -#include <media/videobuf-core.h> +#include <media/videobuf2-v4l2.h> #include "atomisp_compat.h" @@ -64,8 +64,4 @@ struct atomisp_fmt { u32 bayer_order; }; -struct atomisp_buffer { - struct videobuf_buffer vb; -}; - #endif diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat.h b/drivers/staging/media/atomisp/pci/atomisp_compat.h index a6d85d0f9ae5..7316eb9f974a 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_compat.h +++ b/drivers/staging/media/atomisp/pci/atomisp_compat.h @@ -22,7 +22,6 @@ #include "atomisp_compat_css20.h" #include "../../include/linux/atomisp.h" -#include <media/videobuf-vmalloc.h> struct atomisp_device; struct atomisp_sub_device; @@ -42,10 +41,6 @@ int atomisp_css_init(struct atomisp_device *isp); void atomisp_css_uninit(struct atomisp_device *isp); -void atomisp_css_suspend(struct atomisp_device *isp); - -int atomisp_css_resume(struct atomisp_device *isp); - void atomisp_css_init_struct(struct atomisp_sub_device *asd); int atomisp_css_irq_translate(struct atomisp_device *isp, @@ -61,7 +56,7 @@ int atomisp_css_irq_enable(struct atomisp_device *isp, enum ia_css_irq_info info, bool enable); int atomisp_q_video_buffer_to_css(struct atomisp_sub_device *asd, - struct videobuf_vmalloc_memory *vm_mem, + struct ia_css_frame *frame, enum atomisp_input_stream_id stream_id, enum ia_css_buffer_type css_buf_type, enum ia_css_pipe_id css_pipe_id); @@ -258,13 +253,6 @@ int atomisp_css_yuvpp_configure_output(struct atomisp_sub_device *asd, unsigned int padded_width, enum ia_css_frame_format format); -int atomisp_css_yuvpp_configure_viewfinder( - struct atomisp_sub_device *asd, - unsigned int stream_index, - unsigned int width, unsigned int height, - unsigned int min_width, - enum ia_css_frame_format format); - int atomisp_css_yuvpp_get_output_frame_info( struct atomisp_sub_device *asd, unsigned int stream_index, diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c index fdc05548d972..61e2e63a0ef1 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c +++ b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c @@ -237,18 +237,6 @@ static void __dump_pipe_config(struct atomisp_sub_device *asd, "pipe_config.isp_pipe_version:%d.\n", p_config->isp_pipe_version); dev_dbg(isp->dev, - "pipe_config.acc_extension=%p.\n", - p_config->acc_extension); - dev_dbg(isp->dev, - "pipe_config.acc_stages=%p.\n", - p_config->acc_stages); - dev_dbg(isp->dev, - "pipe_config.num_acc_stages=%d.\n", - p_config->num_acc_stages); - dev_dbg(isp->dev, - "pipe_config.acc_num_execs=%d.\n", - p_config->acc_num_execs); - dev_dbg(isp->dev, "pipe_config.default_capture_config.capture_mode=%d.\n", p_config->default_capture_config.mode); dev_dbg(isp->dev, @@ -629,10 +617,6 @@ static void __apply_additional_pipe_config( else stream_env->pipe_configs[pipe_id].enable_dz = false; break; - case IA_CSS_PIPE_ID_ACC: - stream_env->pipe_configs[pipe_id].mode = IA_CSS_PIPE_MODE_ACC; - stream_env->pipe_configs[pipe_id].enable_dz = false; - break; default: break; } @@ -644,7 +628,7 @@ static bool is_pipe_valid_to_current_run_mode(struct atomisp_sub_device *asd, if (!asd) return false; - if (pipe_id == IA_CSS_PIPE_ID_ACC || pipe_id == IA_CSS_PIPE_ID_YUVPP) + if (pipe_id == IA_CSS_PIPE_ID_YUVPP) return true; if (asd->vfpp) { @@ -718,12 +702,7 @@ static int __create_pipe(struct atomisp_sub_device *asd, if (pipe_id >= IA_CSS_PIPE_ID_NUM) return -EINVAL; - if (pipe_id != IA_CSS_PIPE_ID_ACC && - !stream_env->pipe_configs[pipe_id].output_info[0].res.width) - return 0; - - if (pipe_id == IA_CSS_PIPE_ID_ACC && - !stream_env->pipe_configs[pipe_id].acc_extension) + if (!stream_env->pipe_configs[pipe_id].output_info[0].res.width) return 0; if (!is_pipe_valid_to_current_run_mode(asd, pipe_id)) @@ -885,48 +864,10 @@ int atomisp_css_load_firmware(struct atomisp_device *isp) void atomisp_css_uninit(struct atomisp_device *isp) { - struct atomisp_sub_device *asd; - unsigned int i; - - for (i = 0; i < isp->num_of_streams; i++) { - asd = &isp->asd[i]; - memset(&asd->params.config, 0, sizeof(asd->params.config)); - asd->params.css_update_params_needed = false; - } - - isp->css_initialized = false; - ia_css_uninit(); -} - -void atomisp_css_suspend(struct atomisp_device *isp) -{ isp->css_initialized = false; ia_css_uninit(); } -int atomisp_css_resume(struct atomisp_device *isp) -{ - unsigned int mmu_base_addr; - int ret; - - ret = hmm_get_mmu_base_addr(isp->dev, &mmu_base_addr); - if (ret) { - dev_err(isp->dev, "get base address error.\n"); - return -EINVAL; - } - - ret = ia_css_init(isp->dev, &isp->css_env.isp_css_env, NULL, - mmu_base_addr, IA_CSS_IRQ_TYPE_PULSE); - if (ret) { - dev_err(isp->dev, "re-init css failed.\n"); - return -EINVAL; - } - ia_css_enable_isys_event_queue(true); - - isp->css_initialized = true; - return 0; -} - int atomisp_css_irq_translate(struct atomisp_device *isp, unsigned int *infos) { @@ -996,7 +937,7 @@ void atomisp_css_init_struct(struct atomisp_sub_device *asd) } int atomisp_q_video_buffer_to_css(struct atomisp_sub_device *asd, - struct videobuf_vmalloc_memory *vm_mem, + struct ia_css_frame *frame, enum atomisp_input_stream_id stream_id, enum ia_css_buffer_type css_buf_type, enum ia_css_pipe_id css_pipe_id) @@ -1006,7 +947,7 @@ int atomisp_q_video_buffer_to_css(struct atomisp_sub_device *asd, int err; css_buf.type = css_buf_type; - css_buf.data.frame = vm_mem->vaddr; + css_buf.data.frame = frame; err = ia_css_pipe_enqueue_buffer( stream_env->pipes[css_pipe_id], &css_buf); @@ -2141,8 +2082,6 @@ static enum ia_css_pipe_mode __pipe_id_to_pipe_mode( return IA_CSS_PIPE_MODE_CAPTURE; case IA_CSS_PIPE_ID_VIDEO: return IA_CSS_PIPE_MODE_VIDEO; - case IA_CSS_PIPE_ID_ACC: - return IA_CSS_PIPE_MODE_ACC; case IA_CSS_PIPE_ID_YUVPP: return IA_CSS_PIPE_MODE_YUVPP; default: @@ -2688,7 +2627,7 @@ int atomisp_get_css_frame_info(struct atomisp_sub_device *asd, if (0 != ia_css_pipe_get_info(asd->stream_env[stream_index] .pipes[pipe_index], &info)) { - dev_err(isp->dev, "ia_css_pipe_get_info FAILED"); + dev_dbg(isp->dev, "ia_css_pipe_get_info FAILED"); return -EINVAL; } @@ -2765,29 +2704,6 @@ int atomisp_css_yuvpp_configure_output(struct atomisp_sub_device *asd, return 0; } -int atomisp_css_yuvpp_configure_viewfinder( - struct atomisp_sub_device *asd, - unsigned int stream_index, - unsigned int width, unsigned int height, - unsigned int min_width, - enum ia_css_frame_format format) -{ - struct atomisp_stream_env *stream_env = - &asd->stream_env[stream_index]; - enum ia_css_pipe_id pipe_id = IA_CSS_PIPE_ID_YUVPP; - - stream_env->pipe_configs[pipe_id].mode = - __pipe_id_to_pipe_mode(asd, pipe_id); - stream_env->update_pipe[pipe_id] = true; - - stream_env->pipe_configs[pipe_id].vf_output_info[0].res.width = width; - stream_env->pipe_configs[pipe_id].vf_output_info[0].res.height = height; - stream_env->pipe_configs[pipe_id].vf_output_info[0].format = format; - stream_env->pipe_configs[pipe_id].vf_output_info[0].padded_width = - min_width; - return 0; -} - int atomisp_css_yuvpp_get_output_frame_info( struct atomisp_sub_device *asd, unsigned int stream_index, @@ -3180,7 +3096,7 @@ static int atomisp_compare_dvs_grid(struct atomisp_sub_device *asd, } if (sizeof(*cur) != sizeof(*atomgrid)) { - dev_err(asd->isp->dev, "dvs grid mis-match!\n"); + dev_err(asd->isp->dev, "dvs grid mismatch!\n"); return -EINVAL; } diff --git a/drivers/staging/media/atomisp/pci/atomisp_fops.c b/drivers/staging/media/atomisp/pci/atomisp_fops.c index 84a84e0cdeef..acea7492847d 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_fops.c +++ b/drivers/staging/media/atomisp/pci/atomisp_fops.c @@ -22,7 +22,7 @@ #include <linux/pm_runtime.h> #include <media/v4l2-ioctl.h> -#include <media/videobuf-vmalloc.h> +#include <media/videobuf2-vmalloc.h> #include "atomisp_cmd.h" #include "atomisp_common.h" @@ -35,49 +35,74 @@ #include "atomisp-regs.h" #include "hmm/hmm.h" +#include "ia_css_frame.h" #include "type_support.h" #include "device_access/device_access.h" -#define ISP_LEFT_PAD 128 /* equal to 2*NWAY */ - /* - * input image data, and current frame resolution for test + * Videobuf2 ops */ -#define ISP_PARAM_MMAP_OFFSET 0xfffff000 +static int atomisp_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct atomisp_video_pipe *pipe = container_of(vq, struct atomisp_video_pipe, vb_queue); + u16 source_pad = atomisp_subdev_source_pad(&pipe->vdev); + int ret; -#define MAGIC_CHECK(is, should) \ - do { \ - if (unlikely((is) != (should))) { \ - pr_err("magic mismatch: %x (expected %x)\n", \ - is, should); \ - BUG(); \ - } \ - } while (0) + mutex_lock(&pipe->asd->isp->mutex); /* for get_css_frame_info() / set_fmt() */ -/* - * Videobuf ops - */ -static int atomisp_buf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - struct atomisp_video_pipe *pipe = vq->priv_data; + /* + * When VIDIOC_S_FMT has not been called before VIDIOC_REQBUFS, then + * this will fail. Call atomisp_set_fmt() ourselves and try again. + */ + ret = atomisp_get_css_frame_info(pipe->asd, source_pad, &pipe->frame_info); + if (ret) { + struct v4l2_format f = { + .fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420, + .fmt.pix.width = 10000, + .fmt.pix.height = 10000, + }; + + ret = atomisp_set_fmt(&pipe->vdev, &f); + if (ret) + goto out; + + ret = atomisp_get_css_frame_info(pipe->asd, source_pad, &pipe->frame_info); + if (ret) + goto out; + } + + atomisp_alloc_css_stat_bufs(pipe->asd, ATOMISP_INPUT_STREAM_GENERAL); - *size = pipe->pix.sizeimage; + *nplanes = 1; + sizes[0] = PAGE_ALIGN(pipe->pix.sizeimage); +out: + mutex_unlock(&pipe->asd->isp->mutex); return 0; } -static int atomisp_buf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, - enum v4l2_field field) +static int atomisp_buf_init(struct vb2_buffer *vb) { - struct atomisp_video_pipe *pipe = vq->priv_data; + struct atomisp_video_pipe *pipe = vb_to_pipe(vb); + struct ia_css_frame *frame = vb_to_frame(vb); + int ret; + + ret = ia_css_frame_init_from_info(frame, &pipe->frame_info); + if (ret) + return ret; - vb->size = pipe->pix.sizeimage; - vb->width = pipe->pix.width; - vb->height = pipe->pix.height; - vb->field = field; - vb->state = VIDEOBUF_PREPARED; + if (frame->data_bytes > vb2_plane_size(vb, 0)) { + dev_err(pipe->asd->isp->dev, "Internal error frame.data_bytes(%u) > vb.length(%lu)\n", + frame->data_bytes, vb2_plane_size(vb, 0)); + return -EIO; + } + + frame->data = hmm_create_from_vmalloc_buf(vb2_plane_size(vb, 0), + vb2_plane_vaddr(vb, 0)); + if (frame->data == mmgr_NULL) + return -ENOMEM; return 0; } @@ -156,8 +181,7 @@ static int atomisp_q_one_s3a_buffer(struct atomisp_sub_device *asd, } else { list_add_tail(&s3a_buf->list, &asd->s3a_stats_in_css); if (s3a_list == &asd->s3a_stats_ready) - dev_warn(asd->isp->dev, "%s: drop one s3a stat which has exp_id %d!\n", - __func__, exp_id); + dev_dbg(asd->isp->dev, "drop one s3a stat with exp_id %d\n", exp_id); } asd->s3a_bufs_in_css[css_pipe_id]++; @@ -206,43 +230,44 @@ static int atomisp_q_one_dis_buffer(struct atomisp_sub_device *asd, return 0; } -int atomisp_q_video_buffers_to_css(struct atomisp_sub_device *asd, - struct atomisp_video_pipe *pipe, - enum atomisp_input_stream_id stream_id, - enum ia_css_buffer_type css_buf_type, - enum ia_css_pipe_id css_pipe_id) +static int atomisp_q_video_buffers_to_css(struct atomisp_sub_device *asd, + struct atomisp_video_pipe *pipe, + enum atomisp_input_stream_id stream_id, + enum ia_css_buffer_type css_buf_type, + enum ia_css_pipe_id css_pipe_id) { - struct videobuf_vmalloc_memory *vm_mem; struct atomisp_css_params_with_list *param; struct ia_css_dvs_grid_info *dvs_grid = atomisp_css_get_dvs_grid_info(&asd->params.curr_grid_info); unsigned long irqflags; - int err = 0; + int space, err = 0; + + lockdep_assert_held(&asd->isp->mutex); if (WARN_ON(css_pipe_id >= IA_CSS_PIPE_ID_NUM)) return -EINVAL; - while (pipe->buffers_in_css < ATOMISP_CSS_Q_DEPTH) { - struct videobuf_buffer *vb; + if (pipe->stopping) + return -EINVAL; + + space = ATOMISP_CSS_Q_DEPTH - atomisp_buffers_in_css(pipe); + while (space--) { + struct ia_css_frame *frame; spin_lock_irqsave(&pipe->irq_lock, irqflags); - if (list_empty(&pipe->activeq)) { - spin_unlock_irqrestore(&pipe->irq_lock, irqflags); - return -EINVAL; - } - vb = list_entry(pipe->activeq.next, - struct videobuf_buffer, queue); - list_del_init(&vb->queue); - vb->state = VIDEOBUF_ACTIVE; + frame = list_first_entry_or_null(&pipe->activeq, struct ia_css_frame, queue); + if (frame) + list_move_tail(&frame->queue, &pipe->buffers_in_css); spin_unlock_irqrestore(&pipe->irq_lock, irqflags); + if (!frame) + return -EINVAL; + /* * If there is a per_frame setting to apply on the buffer, * do it before buffer en-queueing. */ - vm_mem = vb->priv; - - param = pipe->frame_params[vb->i]; + param = pipe->frame_params[frame->vb.vb2_buf.index]; if (param) { atomisp_makeup_css_parameters(asd, &asd->params.css_param.update_flag, @@ -256,8 +281,7 @@ int atomisp_q_video_buffers_to_css(struct atomisp_sub_device *asd, if (!err) asd->params.config.dz_config = ¶m->params.dz_config; } - atomisp_css_set_isp_config_applied_frame(asd, - vm_mem->vaddr); + atomisp_css_set_isp_config_applied_frame(asd, frame); atomisp_css_update_isp_params_on_pipe(asd, asd->stream_env[stream_id].pipes[css_pipe_id]); asd->params.dvs_6axis = (struct ia_css_dvs_6axis_config *) @@ -282,20 +306,19 @@ int atomisp_q_video_buffers_to_css(struct atomisp_sub_device *asd, &asd->params.css_param.dz_config; asd->params.css_update_params_needed = true; } + pipe->frame_params[frame->vb.vb2_buf.index] = NULL; } /* Enqueue buffer */ - err = atomisp_q_video_buffer_to_css(asd, vm_mem, stream_id, + err = atomisp_q_video_buffer_to_css(asd, frame, stream_id, css_buf_type, css_pipe_id); if (err) { spin_lock_irqsave(&pipe->irq_lock, irqflags); - list_add_tail(&vb->queue, &pipe->activeq); - vb->state = VIDEOBUF_QUEUED; + list_move_tail(&frame->queue, &pipe->activeq); spin_unlock_irqrestore(&pipe->irq_lock, irqflags); dev_err(asd->isp->dev, "%s, css q fails: %d\n", __func__, err); return -EINVAL; } - pipe->buffers_in_css++; /* enqueue 3A/DIS/metadata buffers */ if (asd->params.curr_grid_info.s3a_grid.enable && @@ -517,11 +540,32 @@ int atomisp_qbuffers_to_css(struct atomisp_sub_device *asd) return 0; } -static void atomisp_buf_queue(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static void atomisp_buf_queue(struct vb2_buffer *vb) { - struct atomisp_video_pipe *pipe = vq->priv_data; + struct atomisp_video_pipe *pipe = vb_to_pipe(vb); + struct ia_css_frame *frame = vb_to_frame(vb); + struct atomisp_sub_device *asd = pipe->asd; + u16 source_pad = atomisp_subdev_source_pad(&pipe->vdev); + unsigned long irqflags; + int ret; + + mutex_lock(&asd->isp->mutex); + + ret = atomisp_pipe_check(pipe, false); + if (ret || pipe->stopping) { + spin_lock_irqsave(&pipe->irq_lock, irqflags); + atomisp_buffer_done(frame, VB2_BUF_STATE_ERROR); + spin_unlock_irqrestore(&pipe->irq_lock, irqflags); + goto out_unlock; + } + + /* FIXME this ugliness comes from the original atomisp buffer handling */ + if (!(vb->skip_cache_sync_on_finish && vb->skip_cache_sync_on_prepare)) + wbinvd(); + pipe->frame_params[vb->index] = NULL; + + spin_lock_irqsave(&pipe->irq_lock, irqflags); /* * when a frame buffer meets following conditions, it should be put into * the waiting list: @@ -533,40 +577,83 @@ static void atomisp_buf_queue(struct videobuf_queue *vq, * get enqueued. */ if (!atomisp_is_vf_pipe(pipe) && - (pipe->frame_request_config_id[vb->i] || + (pipe->frame_request_config_id[vb->index] || !list_empty(&pipe->buffers_waiting_for_param))) - list_add_tail(&vb->queue, &pipe->buffers_waiting_for_param); + list_add_tail(&frame->queue, &pipe->buffers_waiting_for_param); else - list_add_tail(&vb->queue, &pipe->activeq); + list_add_tail(&frame->queue, &pipe->activeq); + + spin_unlock_irqrestore(&pipe->irq_lock, irqflags); + + /* TODO: do this better, not best way to queue to css */ + if (asd->streaming == ATOMISP_DEVICE_STREAMING_ENABLED) { + if (!list_empty(&pipe->buffers_waiting_for_param)) + atomisp_handle_parameter_and_buffer(pipe); + else + atomisp_qbuffers_to_css(asd); + } + + /* + * Workaround: Due to the design of HALv3, + * sometimes in ZSL or SDV mode HAL needs to + * capture multiple images within one streaming cycle. + * But the capture number cannot be determined by HAL. + * So HAL only sets the capture number to be 1 and queue multiple + * buffers. Atomisp driver needs to check this case and re-trigger + * CSS to do capture when new buffer is queued. + */ + if (asd->continuous_mode->val && source_pad == ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE && + !asd->enable_raw_buffer_lock->val && asd->params.offline_parm.num_captures == 1) { + asd->pending_capture_request++; + dev_dbg(asd->isp->dev, "Add one pending capture request.\n"); + } - vb->state = VIDEOBUF_QUEUED; +out_unlock: + mutex_unlock(&asd->isp->mutex); } -static void atomisp_buf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static void atomisp_buf_cleanup(struct vb2_buffer *vb) { - vb->state = VIDEOBUF_NEEDS_INIT; - atomisp_videobuf_free_buf(vb); + struct atomisp_video_pipe *pipe = vb_to_pipe(vb); + struct ia_css_frame *frame = vb_to_frame(vb); + int index = frame->vb.vb2_buf.index; + + pipe->frame_request_config_id[index] = 0; + pipe->frame_params[index] = NULL; + + hmm_free(frame->data); } -static const struct videobuf_queue_ops videobuf_qops = { - .buf_setup = atomisp_buf_setup, - .buf_prepare = atomisp_buf_prepare, - .buf_queue = atomisp_buf_queue, - .buf_release = atomisp_buf_release, +static const struct vb2_ops atomisp_vb2_ops = { + .queue_setup = atomisp_queue_setup, + .buf_init = atomisp_buf_init, + .buf_cleanup = atomisp_buf_cleanup, + .buf_queue = atomisp_buf_queue, + .start_streaming = atomisp_start_streaming, + .stop_streaming = atomisp_stop_streaming, }; static int atomisp_init_pipe(struct atomisp_video_pipe *pipe) { + int ret; + /* init locks */ spin_lock_init(&pipe->irq_lock); + mutex_init(&pipe->vb_queue_mutex); + + /* Init videobuf2 queue structure */ + pipe->vb_queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + pipe->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR; + pipe->vb_queue.buf_struct_size = sizeof(struct ia_css_frame); + pipe->vb_queue.ops = &atomisp_vb2_ops; + pipe->vb_queue.mem_ops = &vb2_vmalloc_memops; + pipe->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + ret = vb2_queue_init(&pipe->vb_queue); + if (ret) + return ret; - videobuf_queue_vmalloc_init(&pipe->capq, &videobuf_qops, NULL, - &pipe->irq_lock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_NONE, - sizeof(struct atomisp_buffer), pipe, - NULL); /* ext_lock: NULL */ + pipe->vdev.queue = &pipe->vb_queue; + pipe->vdev.queue->lock = &pipe->vb_queue_mutex; INIT_LIST_HEAD(&pipe->activeq); INIT_LIST_HEAD(&pipe->buffers_waiting_for_param); @@ -721,13 +808,6 @@ static int atomisp_open(struct file *file) goto error; } - /* Init ISP */ - if (atomisp_css_init(isp)) { - ret = -EINVAL; - /* Need to clean up CSS init if it fails. */ - goto css_error; - } - atomisp_dev_init_struct(isp); ret = v4l2_subdev_call(isp->flash, core, s_power, 1); @@ -752,7 +832,6 @@ done: return 0; css_error: - atomisp_css_uninit(isp); pm_runtime_put(vdev->v4l2_dev->dev); error: mutex_unlock(&isp->mutex); @@ -766,45 +845,26 @@ static int atomisp_release(struct file *file) struct atomisp_device *isp = video_get_drvdata(vdev); struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); struct atomisp_sub_device *asd = pipe->asd; - struct v4l2_requestbuffers req; struct v4l2_subdev_fh fh; struct v4l2_rect clear_compose = {0}; unsigned long flags; - int ret = 0; + int ret; v4l2_fh_init(&fh.vfh, vdev); - req.count = 0; - if (!isp) - return -EBADF; - - mutex_lock(&isp->mutex); - dev_dbg(isp->dev, "release device %s\n", vdev->name); asd->subdev.devnode = vdev; - pipe->users--; - - if (pipe->capq.streaming) - dev_warn(isp->dev, - "%s: ISP still streaming while closing!", - __func__); + /* Note file must not be used after this! */ + vb2_fop_release(file); - if (pipe->capq.streaming && - atomisp_streamoff(file, NULL, V4L2_BUF_TYPE_VIDEO_CAPTURE)) { - dev_err(isp->dev, "atomisp_streamoff failed on release, driver bug"); - goto done; - } + mutex_lock(&isp->mutex); + pipe->users--; if (pipe->users) goto done; - if (atomisp_reqbufs(file, NULL, &req)) { - dev_err(isp->dev, "atomisp_reqbufs failed on release, driver bug"); - goto done; - } - /* * A little trick here: * file injection input resolution is recorded in the sink pad, @@ -840,7 +900,6 @@ static int atomisp_release(struct file *file) goto done; atomisp_destroy_pipes_stream_force(asd); - atomisp_css_uninit(isp); if (defer_fw_load) { ia_css_unload_firmware(); @@ -862,260 +921,15 @@ done: V4L2_SEL_TGT_COMPOSE, 0, &clear_compose); mutex_unlock(&isp->mutex); - - return v4l2_fh_release(file); -} - -/* - * Memory help functions for image frame and private parameters - */ -static int do_isp_mm_remap(struct atomisp_device *isp, - struct vm_area_struct *vma, - ia_css_ptr isp_virt, u32 host_virt, u32 pgnr) -{ - u32 pfn; - - while (pgnr) { - pfn = hmm_virt_to_phys(isp_virt) >> PAGE_SHIFT; - if (remap_pfn_range(vma, host_virt, pfn, - PAGE_SIZE, PAGE_SHARED)) { - dev_err(isp->dev, "remap_pfn_range err.\n"); - return -EAGAIN; - } - - isp_virt += PAGE_SIZE; - host_virt += PAGE_SIZE; - pgnr--; - } - - return 0; -} - -static int frame_mmap(struct atomisp_device *isp, - const struct ia_css_frame *frame, struct vm_area_struct *vma) -{ - ia_css_ptr isp_virt; - u32 host_virt; - u32 pgnr; - - if (!frame) { - dev_err(isp->dev, "%s: NULL frame pointer.\n", __func__); - return -EINVAL; - } - - host_virt = vma->vm_start; - isp_virt = frame->data; - pgnr = DIV_ROUND_UP(frame->data_bytes, PAGE_SIZE); - - if (do_isp_mm_remap(isp, vma, isp_virt, host_virt, pgnr)) - return -EAGAIN; - return 0; } -int atomisp_videobuf_mmap_mapper(struct videobuf_queue *q, - struct vm_area_struct *vma) -{ - u32 offset = vma->vm_pgoff << PAGE_SHIFT; - int ret = -EINVAL, i; - struct atomisp_device *isp = - ((struct atomisp_video_pipe *)(q->priv_data))->isp; - struct videobuf_vmalloc_memory *vm_mem; - struct videobuf_mapping *map; - - MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) { - dev_err(isp->dev, "map appl bug: PROT_WRITE and MAP_SHARED are required\n"); - return -EINVAL; - } - - mutex_lock(&q->vb_lock); - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - struct videobuf_buffer *buf = q->bufs[i]; - - if (!buf) - continue; - - map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL); - if (!map) { - mutex_unlock(&q->vb_lock); - return -ENOMEM; - } - - buf->map = map; - map->q = q; - - buf->baddr = vma->vm_start; - - if (buf && buf->memory == V4L2_MEMORY_MMAP && - buf->boff == offset) { - vm_mem = buf->priv; - ret = frame_mmap(isp, vm_mem->vaddr, vma); - vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; - break; - } - } - mutex_unlock(&q->vb_lock); - - return ret; -} - -/* The input frame contains left and right padding that need to be removed. - * There is always ISP_LEFT_PAD padding on the left side. - * There is also padding on the right (padded_width - width). - */ -static int remove_pad_from_frame(struct atomisp_device *isp, - struct ia_css_frame *in_frame, __u32 width, __u32 height) -{ - unsigned int i; - unsigned short *buffer; - int ret = 0; - ia_css_ptr load = in_frame->data; - ia_css_ptr store = load; - - buffer = kmalloc_array(width, sizeof(load), GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - load += ISP_LEFT_PAD; - for (i = 0; i < height; i++) { - ret = hmm_load(load, buffer, width * sizeof(load)); - if (ret < 0) - goto remove_pad_error; - - ret = hmm_store(store, buffer, width * sizeof(store)); - if (ret < 0) - goto remove_pad_error; - - load += in_frame->info.padded_width; - store += width; - } - -remove_pad_error: - kfree(buffer); - return ret; -} - -static int atomisp_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct video_device *vdev = video_devdata(file); - struct atomisp_device *isp = video_get_drvdata(vdev); - struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); - struct atomisp_sub_device *asd = pipe->asd; - struct ia_css_frame *raw_virt_addr; - u32 start = vma->vm_start; - u32 end = vma->vm_end; - u32 size = end - start; - u32 origin_size, new_size; - int ret; - - if (!asd) { - dev_err(isp->dev, "%s(): asd is NULL, device is %s\n", - __func__, vdev->name); - return -EINVAL; - } - - if (!(vma->vm_flags & (VM_WRITE | VM_READ))) - return -EACCES; - - mutex_lock(&isp->mutex); - - if (!(vma->vm_flags & VM_SHARED)) { - /* Map private buffer. - * Set VM_SHARED to the flags since we need - * to map the buffer page by page. - * Without VM_SHARED, remap_pfn_range() treats - * this kind of mapping as invalid. - */ - vma->vm_flags |= VM_SHARED; - ret = hmm_mmap(vma, vma->vm_pgoff << PAGE_SHIFT); - mutex_unlock(&isp->mutex); - return ret; - } - - /* mmap for ISP offline raw data */ - if (atomisp_subdev_source_pad(vdev) - == ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE && - vma->vm_pgoff == (ISP_PARAM_MMAP_OFFSET >> PAGE_SHIFT)) { - new_size = pipe->pix.width * pipe->pix.height * 2; - if (asd->params.online_process != 0) { - ret = -EINVAL; - goto error; - } - raw_virt_addr = asd->raw_output_frame; - if (!raw_virt_addr) { - dev_err(isp->dev, "Failed to request RAW frame\n"); - ret = -EINVAL; - goto error; - } - - ret = remove_pad_from_frame(isp, raw_virt_addr, - pipe->pix.width, pipe->pix.height); - if (ret < 0) { - dev_err(isp->dev, "remove pad failed.\n"); - goto error; - } - origin_size = raw_virt_addr->data_bytes; - raw_virt_addr->data_bytes = new_size; - - if (size != PAGE_ALIGN(new_size)) { - dev_err(isp->dev, "incorrect size for mmap ISP Raw Frame\n"); - ret = -EINVAL; - goto error; - } - - if (frame_mmap(isp, raw_virt_addr, vma)) { - dev_err(isp->dev, "frame_mmap failed.\n"); - raw_virt_addr->data_bytes = origin_size; - ret = -EAGAIN; - goto error; - } - raw_virt_addr->data_bytes = origin_size; - vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; - mutex_unlock(&isp->mutex); - return 0; - } - - /* - * mmap for normal frames - */ - if (size != pipe->pix.sizeimage) { - dev_err(isp->dev, "incorrect size for mmap ISP frames\n"); - ret = -EINVAL; - goto error; - } - mutex_unlock(&isp->mutex); - - return atomisp_videobuf_mmap_mapper(&pipe->capq, vma); - -error: - mutex_unlock(&isp->mutex); - - return ret; -} - -static __poll_t atomisp_poll(struct file *file, - struct poll_table_struct *pt) -{ - struct video_device *vdev = video_devdata(file); - struct atomisp_device *isp = video_get_drvdata(vdev); - struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); - - mutex_lock(&isp->mutex); - if (pipe->capq.streaming != 1) { - mutex_unlock(&isp->mutex); - return EPOLLERR; - } - mutex_unlock(&isp->mutex); - - return videobuf_poll_stream(file, &pipe->capq, pt); -} - const struct v4l2_file_operations atomisp_fops = { .owner = THIS_MODULE, .open = atomisp_open, .release = atomisp_release, - .mmap = atomisp_mmap, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, .unlocked_ioctl = video_ioctl2, #ifdef CONFIG_COMPAT /* @@ -1124,5 +938,4 @@ const struct v4l2_file_operations atomisp_fops = { .compat_ioctl32 = atomisp_compat_ioctl32, */ #endif - .poll = atomisp_poll, }; diff --git a/drivers/staging/media/atomisp/pci/atomisp_fops.h b/drivers/staging/media/atomisp/pci/atomisp_fops.h index 3f1e442ba782..10e43126b693 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_fops.h +++ b/drivers/staging/media/atomisp/pci/atomisp_fops.h @@ -22,12 +22,6 @@ #define __ATOMISP_FOPS_H__ #include "atomisp_subdev.h" -int atomisp_q_video_buffers_to_css(struct atomisp_sub_device *asd, - struct atomisp_video_pipe *pipe, - enum atomisp_input_stream_id stream_id, - enum ia_css_buffer_type css_buf_type, - enum ia_css_pipe_id css_pipe_id); - unsigned int atomisp_dev_users(struct atomisp_device *isp); unsigned int atomisp_sub_dev_users(struct atomisp_sub_device *asd); @@ -35,13 +29,6 @@ unsigned int atomisp_sub_dev_users(struct atomisp_sub_device *asd); * Memory help functions for image frame and private parameters */ -int atomisp_videobuf_mmap_mapper(struct videobuf_queue *q, - struct vm_area_struct *vma); - -int atomisp_qbuf_to_css(struct atomisp_device *isp, - struct atomisp_video_pipe *pipe, - struct videobuf_buffer *vb); - int atomisp_qbuffers_to_css(struct atomisp_sub_device *asd); extern const struct v4l2_file_operations atomisp_fops; diff --git a/drivers/staging/media/atomisp/pci/atomisp_internal.h b/drivers/staging/media/atomisp/pci/atomisp_internal.h index d9d158cdf09e..653e6d74a966 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_internal.h +++ b/drivers/staging/media/atomisp/pci/atomisp_internal.h @@ -195,7 +195,6 @@ struct atomisp_regs { }; struct atomisp_sw_contex { - int power_state; int running_freq; }; diff --git a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c index 0ddb0ed42dd9..cb01ba65c88f 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c +++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c @@ -23,7 +23,6 @@ #include <media/v4l2-ioctl.h> #include <media/v4l2-event.h> -#include <media/videobuf-vmalloc.h> #include "atomisp_cmd.h" #include "atomisp_common.h" @@ -542,6 +541,11 @@ int atomisp_pipe_check(struct atomisp_video_pipe *pipe, bool settings_change) if (pipe->isp->isp_fatal_error) return -EIO; + if (settings_change && vb2_is_busy(&pipe->vb_queue)) { + dev_err(pipe->isp->dev, "Set fmt/input IOCTL while streaming\n"); + return -EBUSY; + } + switch (pipe->asd->streaming) { case ATOMISP_DEVICE_STREAMING_DISABLED: break; @@ -632,10 +636,10 @@ static int atomisp_enum_input(struct file *file, void *fh, static unsigned int atomisp_subdev_streaming_count(struct atomisp_sub_device *asd) { - return asd->video_out_preview.capq.streaming - + asd->video_out_capture.capq.streaming - + asd->video_out_video_capture.capq.streaming - + asd->video_out_vf.capq.streaming; + return asd->video_out_preview.vb_queue.start_streaming_called + + asd->video_out_capture.vb_queue.start_streaming_called + + asd->video_out_video_capture.vb_queue.start_streaming_called + + asd->video_out_vf.vb_queue.start_streaming_called; } unsigned int atomisp_streaming_count(struct atomisp_device *isp) @@ -661,6 +665,14 @@ static int atomisp_g_input(struct file *file, void *fh, unsigned int *input) return 0; } +static int atomisp_s_fmt_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct video_device *vdev = video_devdata(file); + + return atomisp_set_fmt(vdev, f); +} + /* * set input are used to set current primary/secondary camera */ @@ -866,29 +878,8 @@ static int atomisp_adjust_fmt(struct v4l2_format *f) u32 padded_width; format_bridge = atomisp_get_format_bridge(f->fmt.pix.pixelformat); - - padded_width = f->fmt.pix.width + pad_w; - - if (format_bridge->planar) { - f->fmt.pix.bytesperline = padded_width; - f->fmt.pix.sizeimage = PAGE_ALIGN(f->fmt.pix.height * - DIV_ROUND_UP(format_bridge->depth * - padded_width, 8)); - } else { - f->fmt.pix.bytesperline = DIV_ROUND_UP(format_bridge->depth * - padded_width, 8); - f->fmt.pix.sizeimage = PAGE_ALIGN(f->fmt.pix.height * f->fmt.pix.bytesperline); - } - - if (f->fmt.pix.field == V4L2_FIELD_ANY) - f->fmt.pix.field = V4L2_FIELD_NONE; - - format_bridge = atomisp_get_format_bridge(f->fmt.pix.pixelformat); - if (!format_bridge) - return -EINVAL; - /* Currently, raw formats are broken!!! */ - if (format_bridge->sh_fmt == IA_CSS_FRAME_FORMAT_RAW) { + if (!format_bridge || format_bridge->sh_fmt == IA_CSS_FRAME_FORMAT_RAW) { f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; format_bridge = atomisp_get_format_bridge(f->fmt.pix.pixelformat); @@ -931,6 +922,7 @@ static int atomisp_try_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) { struct video_device *vdev = video_devdata(file); + u32 pixfmt = f->fmt.pix.pixelformat; int ret; /* @@ -944,6 +936,12 @@ static int atomisp_try_fmt_cap(struct file *file, void *fh, if (ret) return ret; + /* + * atomisp_try_fmt() replaces pixelformat with the sensors native + * format, restore the actual format requested by userspace. + */ + f->fmt.pix.pixelformat = pixfmt; + return atomisp_adjust_fmt(f); } @@ -961,44 +959,13 @@ static int atomisp_g_fmt_cap(struct file *file, void *fh, if (f->fmt.pix.sizeimage) return 0; - f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; f->fmt.pix.width = 10000; f->fmt.pix.height = 10000; return atomisp_try_fmt_cap(file, fh, f); } -/* - * Free videobuffer buffer priv data - */ -void atomisp_videobuf_free_buf(struct videobuf_buffer *vb) -{ - struct videobuf_vmalloc_memory *vm_mem; - - if (!vb) - return; - - vm_mem = vb->priv; - if (vm_mem && vm_mem->vaddr) { - ia_css_frame_free(vm_mem->vaddr); - vm_mem->vaddr = NULL; - } -} - -/* - * this function is used to free video buffer queue - */ -static void atomisp_videobuf_free_queue(struct videobuf_queue *q) -{ - int i; - - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - atomisp_videobuf_free_buf(q->bufs[i]); - kfree(q->bufs[i]); - q->bufs[i] = NULL; - } -} - int atomisp_alloc_css_stat_bufs(struct atomisp_sub_device *asd, uint16_t stream_id) { @@ -1100,178 +1067,13 @@ error: return -ENOMEM; } -/* - * Initiate Memory Mapping or User Pointer I/O - */ -int atomisp_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *req) -{ - struct video_device *vdev = video_devdata(file); - struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); - struct atomisp_sub_device *asd = pipe->asd; - struct ia_css_frame_info frame_info; - struct ia_css_frame *frame; - struct videobuf_vmalloc_memory *vm_mem; - u16 source_pad = atomisp_subdev_source_pad(vdev); - int ret = 0, i = 0; - - if (req->count == 0) { - mutex_lock(&pipe->capq.vb_lock); - if (!list_empty(&pipe->capq.stream)) - videobuf_queue_cancel(&pipe->capq); - - atomisp_videobuf_free_queue(&pipe->capq); - mutex_unlock(&pipe->capq.vb_lock); - /* clear request config id */ - memset(pipe->frame_request_config_id, 0, - VIDEO_MAX_FRAME * sizeof(unsigned int)); - memset(pipe->frame_params, 0, - VIDEO_MAX_FRAME * - sizeof(struct atomisp_css_params_with_list *)); - return 0; - } - - ret = videobuf_reqbufs(&pipe->capq, req); - if (ret) - return ret; - - atomisp_alloc_css_stat_bufs(asd, ATOMISP_INPUT_STREAM_GENERAL); - - /* - * for user pointer type, buffers are not really allocated here, - * buffers are setup in QBUF operation through v4l2_buffer structure - */ - if (req->memory == V4L2_MEMORY_USERPTR) - return 0; - - ret = atomisp_get_css_frame_info(asd, source_pad, &frame_info); - if (ret) - return ret; - - /* - * Allocate the real frame here for selected node using our - * memory management function - */ - for (i = 0; i < req->count; i++) { - if (ia_css_frame_allocate_from_info(&frame, &frame_info)) - goto error; - vm_mem = pipe->capq.bufs[i]->priv; - vm_mem->vaddr = frame; - } - - return ret; - -error: - while (i--) { - vm_mem = pipe->capq.bufs[i]->priv; - ia_css_frame_free(vm_mem->vaddr); - } - - if (asd->vf_frame) - ia_css_frame_free(asd->vf_frame); - - return -ENOMEM; -} - -/* application query the status of a buffer */ -static int atomisp_querybuf(struct file *file, void *fh, - struct v4l2_buffer *buf) -{ - struct video_device *vdev = video_devdata(file); - struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); - - return videobuf_querybuf(&pipe->capq, buf); -} - -/* - * Applications call the VIDIOC_QBUF ioctl to enqueue an empty (capturing) or - * filled (output) buffer in the drivers incoming queue. - */ -static int atomisp_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) +static int atomisp_qbuf_wrapper(struct file *file, void *fh, struct v4l2_buffer *buf) { - static const int NOFLUSH_FLAGS = V4L2_BUF_FLAG_NO_CACHE_INVALIDATE | - V4L2_BUF_FLAG_NO_CACHE_CLEAN; struct video_device *vdev = video_devdata(file); struct atomisp_device *isp = video_get_drvdata(vdev); struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); - struct atomisp_sub_device *asd = pipe->asd; - struct videobuf_buffer *vb; - struct videobuf_vmalloc_memory *vm_mem; - struct ia_css_frame_info frame_info; - struct ia_css_frame *handle = NULL; - u32 length; - u32 pgnr; - int ret; - - ret = atomisp_pipe_check(pipe, false); - if (ret) - return ret; - - if (!buf || buf->index >= VIDEO_MAX_FRAME || - !pipe->capq.bufs[buf->index]) { - dev_err(isp->dev, "Invalid index for qbuf.\n"); - return -EINVAL; - } - - /* - * For userptr type frame, we convert user space address to physic - * address and reprograme out page table properly - */ - if (buf->memory == V4L2_MEMORY_USERPTR) { - if (offset_in_page(buf->m.userptr)) { - dev_err(isp->dev, "Error userptr is not page aligned.\n"); - return -EINVAL; - } - - vb = pipe->capq.bufs[buf->index]; - vm_mem = vb->priv; - if (!vm_mem) - return -EINVAL; - - length = vb->bsize; - pgnr = (length + (PAGE_SIZE - 1)) >> PAGE_SHIFT; - - if (vb->baddr == buf->m.userptr && vm_mem->vaddr) - goto done; - - if (atomisp_get_css_frame_info(asd, - atomisp_subdev_source_pad(vdev), &frame_info)) - return -EIO; - - ret = ia_css_frame_map(&handle, &frame_info, - (void __user *)buf->m.userptr, - pgnr); - if (ret) { - dev_err(isp->dev, "Failed to map user buffer\n"); - return ret; - } - - if (vm_mem->vaddr) { - mutex_lock(&pipe->capq.vb_lock); - ia_css_frame_free(vm_mem->vaddr); - vm_mem->vaddr = NULL; - vb->state = VIDEOBUF_NEEDS_INIT; - mutex_unlock(&pipe->capq.vb_lock); - } - - vm_mem->vaddr = handle; - - buf->flags &= ~V4L2_BUF_FLAG_MAPPED; - buf->flags |= V4L2_BUF_FLAG_QUEUED; - buf->flags &= ~V4L2_BUF_FLAG_DONE; - } else if (buf->memory == V4L2_MEMORY_MMAP) { - buf->flags |= V4L2_BUF_FLAG_MAPPED; - buf->flags |= V4L2_BUF_FLAG_QUEUED; - buf->flags &= ~V4L2_BUF_FLAG_DONE; - - /* - * For mmap, frames were allocated at request buffers - */ - } - -done: - if (!((buf->flags & NOFLUSH_FLAGS) == NOFLUSH_FLAGS)) - wbinvd(); + /* FIXME this abuse of buf->reserved2 comes from the original atomisp buffer handling */ if (!atomisp_is_vf_pipe(pipe) && (buf->reserved2 & ATOMISP_BUFFER_HAS_PER_FRAME_SETTING)) { /* this buffer will have a per-frame parameter */ @@ -1284,90 +1086,27 @@ done: pipe->frame_request_config_id[buf->index] = 0; } - pipe->frame_params[buf->index] = NULL; - - mutex_unlock(&isp->mutex); - ret = videobuf_qbuf(&pipe->capq, buf); - mutex_lock(&isp->mutex); - if (ret) - return ret; - - /* TODO: do this better, not best way to queue to css */ - if (asd->streaming == ATOMISP_DEVICE_STREAMING_ENABLED) { - if (!list_empty(&pipe->buffers_waiting_for_param)) { - atomisp_handle_parameter_and_buffer(pipe); - } else { - atomisp_qbuffers_to_css(asd); - } - } - - /* - * Workaround: Due to the design of HALv3, - * sometimes in ZSL or SDV mode HAL needs to - * capture multiple images within one streaming cycle. - * But the capture number cannot be determined by HAL. - * So HAL only sets the capture number to be 1 and queue multiple - * buffers. Atomisp driver needs to check this case and re-trigger - * CSS to do capture when new buffer is queued. - */ - if (asd->continuous_mode->val && - atomisp_subdev_source_pad(vdev) - == ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE && - pipe->capq.streaming && - !asd->enable_raw_buffer_lock->val && - asd->params.offline_parm.num_captures == 1) { - asd->pending_capture_request++; - dev_dbg(isp->dev, "Add one pending capture request.\n"); - } - - dev_dbg(isp->dev, "qbuf buffer %d (%s) for asd%d\n", buf->index, - vdev->name, asd->index); - - return 0; + return vb2_ioctl_qbuf(file, fh, buf); } -static int __get_frame_exp_id(struct atomisp_video_pipe *pipe, - struct v4l2_buffer *buf) -{ - struct videobuf_vmalloc_memory *vm_mem; - struct ia_css_frame *handle; - int i; - - for (i = 0; pipe->capq.bufs[i]; i++) { - vm_mem = pipe->capq.bufs[i]->priv; - handle = vm_mem->vaddr; - if (buf->index == pipe->capq.bufs[i]->i && handle) - return handle->exp_id; - } - return -EINVAL; -} - -/* - * Applications call the VIDIOC_DQBUF ioctl to dequeue a filled (capturing) or - * displayed (output buffer)from the driver's outgoing queue - */ -static int atomisp_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) +static int atomisp_dqbuf_wrapper(struct file *file, void *fh, struct v4l2_buffer *buf) { struct video_device *vdev = video_devdata(file); struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); struct atomisp_sub_device *asd = pipe->asd; struct atomisp_device *isp = video_get_drvdata(vdev); + struct ia_css_frame *frame; + struct vb2_buffer *vb; int ret; - ret = atomisp_pipe_check(pipe, false); + ret = vb2_ioctl_dqbuf(file, fh, buf); if (ret) return ret; - mutex_unlock(&isp->mutex); - ret = videobuf_dqbuf(&pipe->capq, buf, file->f_flags & O_NONBLOCK); - mutex_lock(&isp->mutex); - if (ret) { - if (ret != -EAGAIN) - dev_dbg(isp->dev, "<%s: %d\n", __func__, ret); - return ret; - } + vb = pipe->vb_queue.bufs[buf->index]; + frame = vb_to_frame(vb); - buf->bytesused = pipe->pix.sizeimage; + /* FIXME this abuse of buf->reserved* comes from the original atomisp buffer handling */ buf->reserved = asd->frame_status[buf->index]; /* @@ -1378,7 +1117,7 @@ static int atomisp_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) */ buf->reserved &= 0x0000ffff; if (!(buf->flags & V4L2_BUF_FLAG_ERROR)) - buf->reserved |= __get_frame_exp_id(pipe, buf) << 16; + buf->reserved |= frame->exp_id; buf->reserved2 = pipe->frame_config_id[buf->index]; dev_dbg(isp->dev, @@ -1506,36 +1245,26 @@ static void atomisp_dma_burst_len_cfg(struct atomisp_sub_device *asd) atomisp_css2_hw_store_32(DMA_BURST_SIZE_REG, 0x00); } -/* - * This ioctl start the capture during streaming I/O. - */ -static int atomisp_streamon(struct file *file, void *fh, - enum v4l2_buf_type type) +int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count) { - struct video_device *vdev = video_devdata(file); - struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); + struct atomisp_video_pipe *pipe = vq_to_pipe(vq); struct atomisp_sub_device *asd = pipe->asd; - struct atomisp_device *isp = video_get_drvdata(vdev); + struct video_device *vdev = &pipe->vdev; + struct atomisp_device *isp = asd->isp; struct pci_dev *pdev = to_pci_dev(isp->dev); enum ia_css_pipe_id css_pipe_id; unsigned int sensor_start_stream; unsigned long irqflags; int ret; + mutex_lock(&isp->mutex); + dev_dbg(isp->dev, "Start stream on pad %d for asd%d\n", atomisp_subdev_source_pad(vdev), asd->index); - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - dev_dbg(isp->dev, "unsupported v4l2 buf type\n"); - return -EINVAL; - } - ret = atomisp_pipe_check(pipe, false); if (ret) - return ret; - - if (pipe->capq.streaming) - return 0; + goto out_unlock; /* Input system HW workaround */ atomisp_dma_burst_len_cfg(asd); @@ -1546,18 +1275,6 @@ static int atomisp_streamon(struct file *file, void *fh, */ sensor_start_stream = atomisp_sensor_start_stream(asd); - spin_lock_irqsave(&pipe->irq_lock, irqflags); - if (list_empty(&pipe->capq.stream)) { - spin_unlock_irqrestore(&pipe->irq_lock, irqflags); - dev_dbg(isp->dev, "no buffer in the queue\n"); - return -EINVAL; - } - spin_unlock_irqrestore(&pipe->irq_lock, irqflags); - - ret = videobuf_streamon(&pipe->capq); - if (ret) - return ret; - /* Reset pending capture request count. */ asd->pending_capture_request = 0; @@ -1578,8 +1295,10 @@ static int atomisp_streamon(struct file *file, void *fh, mutex_unlock(&isp->mutex); ret = wait_for_completion_interruptible(&asd->init_done); mutex_lock(&isp->mutex); - if (ret != 0) - return -ERESTARTSYS; + if (ret) { + ret = -ERESTARTSYS; + goto out_unlock; + } } /* handle per_frame_setting parameter and buffers */ @@ -1601,12 +1320,15 @@ static int atomisp_streamon(struct file *file, void *fh, asd->params.offline_parm.num_captures, asd->params.offline_parm.skip_frames, asd->params.offline_parm.offset); - if (ret) - return -EINVAL; + if (ret) { + ret = -EINVAL; + goto out_unlock; + } } } atomisp_qbuffers_to_css(asd); - return 0; + ret = 0; + goto out_unlock; } if (asd->streaming == ATOMISP_DEVICE_STREAMING_ENABLED) { @@ -1631,8 +1353,10 @@ static int atomisp_streamon(struct file *file, void *fh, asd->params.dvs_6axis = NULL; ret = atomisp_css_start(asd, css_pipe_id, false); - if (ret) - return ret; + if (ret) { + atomisp_flush_video_pipe(pipe, true); + goto out_unlock; + } spin_lock_irqsave(&isp->lock, irqflags); asd->streaming = ATOMISP_DEVICE_STREAMING_ENABLED; @@ -1652,8 +1376,10 @@ static int atomisp_streamon(struct file *file, void *fh, atomisp_qbuffers_to_css(asd); /* Only start sensor when the last streaming instance started */ - if (atomisp_subdev_streaming_count(asd) < sensor_start_stream) - return 0; + if (atomisp_subdev_streaming_count(asd) < sensor_start_stream) { + ret = 0; + goto out_unlock; + } start_sensor: if (isp->flash) { @@ -1684,7 +1410,7 @@ start_sensor: ret = atomisp_stream_on_master_slave_sensor(isp, false); if (ret) { dev_err(isp->dev, "master slave sensor stream on failed!\n"); - return ret; + goto out_unlock; } goto start_delay_wq; } else if (asd->depth_mode->val && (atomisp_streaming_count(isp) < @@ -1706,7 +1432,8 @@ start_sensor: spin_lock_irqsave(&isp->lock, irqflags); asd->streaming = ATOMISP_DEVICE_STREAMING_DISABLED; spin_unlock_irqrestore(&isp->lock, irqflags); - return -EINVAL; + ret = -EINVAL; + goto out_unlock; } start_delay_wq: @@ -1722,35 +1449,44 @@ start_delay_wq: asd->delayed_init = ATOMISP_DELAYED_INIT_NOT_QUEUED; } - return 0; +out_unlock: + mutex_unlock(&isp->mutex); + return ret; } -int atomisp_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) +void atomisp_stop_streaming(struct vb2_queue *vq) { - struct video_device *vdev = video_devdata(file); - struct atomisp_device *isp = video_get_drvdata(vdev); - struct pci_dev *pdev = to_pci_dev(isp->dev); - struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); + struct atomisp_video_pipe *pipe = vq_to_pipe(vq); struct atomisp_sub_device *asd = pipe->asd; - struct atomisp_video_pipe *capture_pipe = NULL; - struct atomisp_video_pipe *vf_pipe = NULL; - struct atomisp_video_pipe *preview_pipe = NULL; - struct atomisp_video_pipe *video_pipe = NULL; - struct videobuf_buffer *vb, *_vb; + struct video_device *vdev = &pipe->vdev; + struct atomisp_device *isp = asd->isp; + struct pci_dev *pdev = to_pci_dev(isp->dev); + bool recreate_streams[MAX_STREAM_NUM] = {0}; enum ia_css_pipe_id css_pipe_id; - int ret; - unsigned long flags; bool first_streamoff = false; + unsigned long flags; + int i, ret; + + mutex_lock(&isp->mutex); dev_dbg(isp->dev, "Stop stream on pad %d for asd%d\n", atomisp_subdev_source_pad(vdev), asd->index); - lockdep_assert_held(&isp->mutex); - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - dev_dbg(isp->dev, "unsupported v4l2 buf type\n"); - return -EINVAL; - } + /* + * There is no guarantee that the buffers queued to / owned by the ISP + * will properly be returned to the queue when stopping. Set a flag to + * avoid new buffers getting queued and then wait for all the current + * buffers to finish. + */ + pipe->stopping = true; + mutex_unlock(&isp->mutex); + /* wait max 1 second */ + ret = wait_event_timeout(pipe->vb_queue.done_wq, + atomisp_buffers_in_css(pipe) == 0, HZ); + mutex_lock(&isp->mutex); + pipe->stopping = false; + if (ret == 0) + dev_warn(isp->dev, "Warning timeout waiting for CSS to return buffers\n"); /* * do only videobuf_streamoff for capture & vf pipes in @@ -1766,36 +1502,10 @@ int atomisp_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) 0, 0, 0); atomisp_freq_scaling(isp, ATOMISP_DFS_MODE_AUTO, false); } - /* - * Currently there is no way to flush buffers queued to css. - * When doing videobuf_streamoff, active buffers will be - * marked as VIDEOBUF_NEEDS_INIT. HAL will be able to use - * these buffers again, and these buffers might be queued to - * css more than once! Warn here, if HAL has not dequeued all - * buffers back before calling streamoff. - */ - if (pipe->buffers_in_css != 0) { - WARN(1, "%s: buffers of vdev %s still in CSS!\n", - __func__, pipe->vdev.name); - - /* - * Buffers remained in css maybe dequeued out in the - * next stream on, while this will causes serious - * issues as buffers already get invalid after - * previous stream off. - * - * No way to flush buffers but to reset the whole css - */ - dev_warn(isp->dev, "Reset CSS to clean up css buffers.\n"); - atomisp_css_flush(isp); - } - return videobuf_streamoff(&pipe->capq); + goto out_unlock; } - if (!pipe->capq.streaming) - return 0; - if (asd->streaming == ATOMISP_DEVICE_STREAMING_ENABLED) first_streamoff = true; @@ -1806,12 +1516,8 @@ int atomisp_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) asd->streaming = ATOMISP_DEVICE_STREAMING_STOPPING; spin_unlock_irqrestore(&isp->lock, flags); - if (!first_streamoff) { - ret = videobuf_streamoff(&pipe->capq); - if (ret) - return ret; + if (!first_streamoff) goto stopsensor; - } atomisp_clear_css_buffer_counters(asd); atomisp_css_irq_enable(isp, IA_CSS_IRQ_INFO_CSS_RECEIVER_SOF, false); @@ -1824,48 +1530,12 @@ int atomisp_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) css_pipe_id = atomisp_get_css_pipe_id(asd); atomisp_css_stop(asd, css_pipe_id, false); - /* cancel work queue*/ - if (asd->video_out_capture.users) { - capture_pipe = &asd->video_out_capture; - wake_up_interruptible(&capture_pipe->capq.wait); - } - if (asd->video_out_vf.users) { - vf_pipe = &asd->video_out_vf; - wake_up_interruptible(&vf_pipe->capq.wait); - } - if (asd->video_out_preview.users) { - preview_pipe = &asd->video_out_preview; - wake_up_interruptible(&preview_pipe->capq.wait); - } - if (asd->video_out_video_capture.users) { - video_pipe = &asd->video_out_video_capture; - wake_up_interruptible(&video_pipe->capq.wait); - } - ret = videobuf_streamoff(&pipe->capq); - if (ret) - return ret; - - /* cleanup css here */ - /* no need for this, as ISP will be reset anyway */ - /*atomisp_flush_bufs_in_css(isp);*/ - - spin_lock_irqsave(&pipe->irq_lock, flags); - list_for_each_entry_safe(vb, _vb, &pipe->activeq, queue) { - vb->state = VIDEOBUF_PREPARED; - list_del(&vb->queue); - } - list_for_each_entry_safe(vb, _vb, &pipe->buffers_waiting_for_param, queue) { - vb->state = VIDEOBUF_PREPARED; - list_del(&vb->queue); - pipe->frame_request_config_id[vb->i] = 0; - } - spin_unlock_irqrestore(&pipe->irq_lock, flags); + atomisp_flush_video_pipe(pipe, true); atomisp_subdev_cleanup_pending_events(asd); stopsensor: - if (atomisp_subdev_streaming_count(asd) + 1 - != atomisp_sensor_start_stream(asd)) - return 0; + if (atomisp_subdev_streaming_count(asd) != atomisp_sensor_start_stream(asd)) + goto out_unlock; ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera, video, s_stream, 0); @@ -1878,7 +1548,7 @@ stopsensor: /* if other streams are running, isp should not be powered off */ if (atomisp_streaming_count(isp)) { atomisp_css_flush(isp); - return 0; + goto out_unlock; } /* Disable the CSI interface on ANN B0/K0 */ @@ -1894,50 +1564,45 @@ stopsensor: * ISP work around, need to reset isp * Is it correct time to reset ISP when first node does streamoff? */ - if (isp->sw_contex.power_state == ATOM_ISP_POWER_UP) { - unsigned int i; - bool recreate_streams[MAX_STREAM_NUM] = {0}; - - if (isp->isp_timeout) - dev_err(isp->dev, "%s: Resetting with WA activated", - __func__); - /* - * It is possible that the other asd stream is in the stage - * that v4l2_setfmt is just get called on it, which will - * create css stream on that stream. But at this point, there - * is no way to destroy the css stream created on that stream. - * - * So force stream destroy here. - */ - for (i = 0; i < isp->num_of_streams; i++) { - if (isp->asd[i].stream_prepared) { - atomisp_destroy_pipes_stream_force(&isp-> - asd[i]); - recreate_streams[i] = true; - } + if (isp->isp_timeout) + dev_err(isp->dev, "%s: Resetting with WA activated", + __func__); + /* + * It is possible that the other asd stream is in the stage + * that v4l2_setfmt is just get called on it, which will + * create css stream on that stream. But at this point, there + * is no way to destroy the css stream created on that stream. + * + * So force stream destroy here. + */ + for (i = 0; i < isp->num_of_streams; i++) { + if (isp->asd[i].stream_prepared) { + atomisp_destroy_pipes_stream_force(&isp->asd[i]); + recreate_streams[i] = true; } + } - /* disable PUNIT/ISP acknowlede/handshake - SRSE=3 */ - pci_write_config_dword(pdev, PCI_I_CONTROL, - isp->saved_regs.i_control | MRFLD_PCI_I_CONTROL_SRSE_RESET_MASK); - dev_err(isp->dev, "atomisp_reset"); - atomisp_reset(isp); - for (i = 0; i < isp->num_of_streams; i++) { - if (recreate_streams[i]) { - int ret2; - - ret2 = atomisp_create_pipes_stream(&isp->asd[i]); - if (ret2) { - dev_err(isp->dev, "%s error re-creating streams: %d\n", - __func__, ret2); - if (!ret) - ret = ret2; - } + /* disable PUNIT/ISP acknowlede/handshake - SRSE=3 */ + pci_write_config_dword(pdev, PCI_I_CONTROL, + isp->saved_regs.i_control | MRFLD_PCI_I_CONTROL_SRSE_RESET_MASK); + dev_err(isp->dev, "atomisp_reset"); + atomisp_reset(isp); + for (i = 0; i < isp->num_of_streams; i++) { + if (recreate_streams[i]) { + int ret2; + + ret2 = atomisp_create_pipes_stream(&isp->asd[i]); + if (ret2) { + dev_err(isp->dev, "%s error re-creating streams: %d\n", + __func__, ret2); + if (!ret) + ret = ret2; } } - isp->isp_timeout = false; } - return ret; + isp->isp_timeout = false; +out_unlock: + mutex_unlock(&isp->mutex); } /* @@ -2725,13 +2390,13 @@ const struct v4l2_ioctl_ops atomisp_ioctl_ops = { .vidioc_enum_fmt_vid_cap = atomisp_enum_fmt_cap, .vidioc_try_fmt_vid_cap = atomisp_try_fmt_cap, .vidioc_g_fmt_vid_cap = atomisp_g_fmt_cap, - .vidioc_s_fmt_vid_cap = atomisp_set_fmt, - .vidioc_reqbufs = atomisp_reqbufs, - .vidioc_querybuf = atomisp_querybuf, - .vidioc_qbuf = atomisp_qbuf, - .vidioc_dqbuf = atomisp_dqbuf, - .vidioc_streamon = atomisp_streamon, - .vidioc_streamoff = atomisp_streamoff, + .vidioc_s_fmt_vid_cap = atomisp_s_fmt_cap, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = atomisp_qbuf_wrapper, + .vidioc_dqbuf = atomisp_dqbuf_wrapper, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_default = atomisp_vidioc_default, .vidioc_s_parm = atomisp_s_parm, .vidioc_g_parm = atomisp_g_parm, diff --git a/drivers/staging/media/atomisp/pci/atomisp_ioctl.h b/drivers/staging/media/atomisp/pci/atomisp_ioctl.h index c660f631d371..59e071f035f9 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.h +++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.h @@ -39,14 +39,12 @@ int atomisp_pipe_check(struct atomisp_video_pipe *pipe, bool streaming_ok); int atomisp_alloc_css_stat_bufs(struct atomisp_sub_device *asd, uint16_t stream_id); -int atomisp_streamoff(struct file *file, void *fh, enum v4l2_buf_type type); -int atomisp_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *req); +int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count); +void atomisp_stop_streaming(struct vb2_queue *vq); enum ia_css_pipe_id atomisp_get_css_pipe_id(struct atomisp_sub_device *asd); -void atomisp_videobuf_free_buf(struct videobuf_buffer *vb); - extern const struct v4l2_ioctl_ops atomisp_ioctl_ops; unsigned int atomisp_streaming_count(struct atomisp_device *isp); @@ -57,4 +55,8 @@ long atomisp_compat_ioctl32(struct file *file, int atomisp_stream_on_master_slave_sensor(struct atomisp_device *isp, bool isp_timeout); + +int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count); +void atomisp_stop_streaming(struct vb2_queue *vq); + #endif /* __ATOMISP_IOCTL_H__ */ diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.c b/drivers/staging/media/atomisp/pci/atomisp_subdev.c index 847dfee6ad78..cadc468b4c2f 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_subdev.c +++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.c @@ -1064,6 +1064,8 @@ static void atomisp_init_subdev_pipe(struct atomisp_sub_device *asd, pipe->asd = asd; pipe->isp = asd->isp; spin_lock_init(&pipe->irq_lock); + mutex_init(&pipe->vb_queue_mutex); + INIT_LIST_HEAD(&pipe->buffers_in_css); INIT_LIST_HEAD(&pipe->activeq); INIT_LIST_HEAD(&pipe->buffers_waiting_for_param); INIT_LIST_HEAD(&pipe->per_frame_params); diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.h b/drivers/staging/media/atomisp/pci/atomisp_subdev.h index a1f4da35235d..bd2872cbb50c 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_subdev.h +++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.h @@ -21,8 +21,7 @@ #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-subdev.h> -#include <media/videobuf-core.h> - +#include <media/videobuf2-v4l2.h> #include "atomisp_common.h" #include "atomisp_compat.h" #include "atomisp_v4l2.h" @@ -69,7 +68,12 @@ struct atomisp_video_pipe { struct video_device vdev; enum v4l2_buf_type type; struct media_pad pad; - struct videobuf_queue capq; + struct vb2_queue vb_queue; + /* Lock for vb_queue, when also taking isp->mutex this must be taken first! */ + struct mutex vb_queue_mutex; + /* List of video-buffers handed over to the CSS */ + struct list_head buffers_in_css; + /* List of video-buffers handed over to the driver, but not yet to the CSS */ struct list_head activeq; /* * the buffers waiting for per-frame parameters, this is only valid @@ -79,10 +83,13 @@ struct atomisp_video_pipe { /* the link list to store per_frame parameters */ struct list_head per_frame_params; + /* Filled through atomisp_get_css_frame_info() on queue setup */ + struct ia_css_frame_info frame_info; + /* Store here the initial run mode */ unsigned int default_run_mode; - - unsigned int buffers_in_css; + /* Set from streamoff to disallow queuing further buffers in CSS */ + bool stopping; /* * irq_lock is used to protect video buffer state change operations and @@ -110,6 +117,11 @@ struct atomisp_video_pipe { struct atomisp_css_params_with_list *frame_params[VIDEO_MAX_FRAME]; }; +#define vq_to_pipe(queue) \ + container_of(queue, struct atomisp_video_pipe, vb_queue) + +#define vb_to_pipe(vb) vq_to_pipe((vb)->vb2_queue) + struct atomisp_pad_format { struct v4l2_mbus_framefmt fmt; struct v4l2_rect crop; diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c index d5bb9906ca6f..e786b81921da 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c +++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c @@ -573,11 +573,7 @@ static int atomisp_mrfld_pre_power_down(struct atomisp_device *isp) unsigned long flags; spin_lock_irqsave(&isp->lock, flags); - if (isp->sw_contex.power_state == ATOM_ISP_POWER_DOWN) { - spin_unlock_irqrestore(&isp->lock, flags); - dev_dbg(isp->dev, "<%s %d.\n", __func__, __LINE__); - return 0; - } + /* * MRFLD HAS requirement: cannot power off i-unit if * ISP has IRQ not serviced. @@ -724,62 +720,51 @@ static int atomisp_mrfld_power(struct atomisp_device *isp, bool enable) return -EBUSY; } -/* Workaround for pmu_nc_set_power_state not ready in MRFLD */ -int atomisp_mrfld_power_down(struct atomisp_device *isp) -{ - return atomisp_mrfld_power(isp, false); -} - -/* Workaround for pmu_nc_set_power_state not ready in MRFLD */ -int atomisp_mrfld_power_up(struct atomisp_device *isp) -{ - return atomisp_mrfld_power(isp, true); -} - -int atomisp_runtime_suspend(struct device *dev) +int atomisp_power_off(struct device *dev) { - struct atomisp_device *isp = (struct atomisp_device *) - dev_get_drvdata(dev); + struct atomisp_device *isp = dev_get_drvdata(dev); + struct pci_dev *pdev = to_pci_dev(dev); int ret; + u32 reg; + + atomisp_css_uninit(isp); ret = atomisp_mrfld_pre_power_down(isp); if (ret) return ret; - /*Turn off the ISP d-phy*/ - ret = atomisp_ospm_dphy_down(isp); - if (ret) - return ret; + /* + * MRFLD IUNIT DPHY is located in an always-power-on island + * MRFLD HW design need all CSI ports are disabled before + * powering down the IUNIT. + */ + pci_read_config_dword(pdev, MRFLD_PCI_CSI_CONTROL, ®); + reg |= MRFLD_ALL_CSI_PORTS_OFF_MASK; + pci_write_config_dword(pdev, MRFLD_PCI_CSI_CONTROL, reg); + cpu_latency_qos_update_request(&isp->pm_qos, PM_QOS_DEFAULT_VALUE); - return atomisp_mrfld_power_down(isp); + return atomisp_mrfld_power(isp, false); } -int atomisp_runtime_resume(struct device *dev) +int atomisp_power_on(struct device *dev) { struct atomisp_device *isp = (struct atomisp_device *) dev_get_drvdata(dev); int ret; - ret = atomisp_mrfld_power_up(isp); + ret = atomisp_mrfld_power(isp, true); if (ret) return ret; cpu_latency_qos_update_request(&isp->pm_qos, isp->max_isr_latency); - if (isp->sw_contex.power_state == ATOM_ISP_POWER_DOWN) { - /*Turn on ISP d-phy */ - ret = atomisp_ospm_dphy_up(isp); - if (ret) { - dev_err(isp->dev, "Failed to power up ISP!.\n"); - return -EINVAL; - } - } /*restore register values for iUnit and iUnitPHY registers*/ if (isp->saved_regs.pcicmdsts) atomisp_restore_iunit_reg(isp); atomisp_freq_scaling(isp, ATOMISP_DFS_MODE_LOW, true); - return 0; + + return atomisp_css_init(isp); } static int __maybe_unused atomisp_suspend(struct device *dev) @@ -789,7 +774,6 @@ static int __maybe_unused atomisp_suspend(struct device *dev) /* FIXME: only has one isp_subdev at present */ struct atomisp_sub_device *asd = &isp->asd[0]; unsigned long flags; - int ret; /* * FIXME: Suspend is not supported by sensors. Abort if any video @@ -806,45 +790,12 @@ static int __maybe_unused atomisp_suspend(struct device *dev) } spin_unlock_irqrestore(&isp->lock, flags); - ret = atomisp_mrfld_pre_power_down(isp); - if (ret) - return ret; - - /*Turn off the ISP d-phy */ - ret = atomisp_ospm_dphy_down(isp); - if (ret) { - dev_err(isp->dev, "fail to power off ISP\n"); - return ret; - } - cpu_latency_qos_update_request(&isp->pm_qos, PM_QOS_DEFAULT_VALUE); - return atomisp_mrfld_power_down(isp); + return atomisp_power_off(dev); } static int __maybe_unused atomisp_resume(struct device *dev) { - struct atomisp_device *isp = (struct atomisp_device *) - dev_get_drvdata(dev); - int ret; - - ret = atomisp_mrfld_power_up(isp); - if (ret) - return ret; - - cpu_latency_qos_update_request(&isp->pm_qos, isp->max_isr_latency); - - /*Turn on ISP d-phy */ - ret = atomisp_ospm_dphy_up(isp); - if (ret) { - dev_err(isp->dev, "Failed to power up ISP!.\n"); - return -EINVAL; - } - - /*restore register values for iUnit and iUnitPHY registers*/ - if (isp->saved_regs.pcicmdsts) - atomisp_restore_iunit_reg(isp); - - atomisp_freq_scaling(isp, ATOMISP_DFS_MODE_LOW, true); - return 0; + return atomisp_power_on(dev); } int atomisp_csi_lane_config(struct atomisp_device *isp) @@ -1459,7 +1410,6 @@ static int atomisp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i isp->dev = &pdev->dev; isp->base = pcim_iomap_table(pdev)[ATOM_ISP_PCI_BAR]; - isp->sw_contex.power_state = ATOM_ISP_POWER_UP; isp->saved_regs.ispmmadr = start; dev_dbg(&pdev->dev, "atomisp mmio base: %p\n", isp->base); @@ -1723,10 +1673,8 @@ load_fw_fail: atomisp_msi_irq_uninit(isp); - atomisp_ospm_dphy_down(isp); - /* Address later when we worry about the ...field chips */ - if (IS_ENABLED(CONFIG_PM) && atomisp_mrfld_power_down(isp)) + if (IS_ENABLED(CONFIG_PM) && atomisp_mrfld_power(isp, false)) dev_err(&pdev->dev, "Failed to switch off ISP\n"); atomisp_dev_alloc_fail: @@ -1774,8 +1722,8 @@ static const struct pci_device_id atomisp_pci_tbl[] = { MODULE_DEVICE_TABLE(pci, atomisp_pci_tbl); static const struct dev_pm_ops atomisp_pm_ops = { - .runtime_suspend = atomisp_runtime_suspend, - .runtime_resume = atomisp_runtime_resume, + .runtime_suspend = atomisp_power_off, + .runtime_resume = atomisp_power_on, .suspend = atomisp_suspend, .resume = atomisp_resume, }; diff --git a/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_binarydesc.h b/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_binarydesc.h index 965cfda50707..e42eeaeb3ee4 100644 --- a/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_binarydesc.h +++ b/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_binarydesc.h @@ -16,6 +16,8 @@ #ifndef __IA_CSS_PIPE_BINARYDESC_H__ #define __IA_CSS_PIPE_BINARYDESC_H__ +#include <linux/math.h> + #include <ia_css_types.h> /* ia_css_pipe */ #include <ia_css_frame_public.h> /* ia_css_frame_info */ #include <ia_css_binary.h> /* ia_css_binary_descr */ @@ -56,17 +58,12 @@ void ia_css_pipe_get_vfpp_binarydesc( * * @param[in] bds_factor: The bayer downscaling factor. * (= The bds_factor member in the sh_css_bds_factor structure.) - * @param[out] bds_factor_numerator: The numerator of the bayer downscaling factor. - * (= The numerator member in the sh_css_bds_factor structure.) - * @param[out] bds_factor_denominator: The denominator of the bayer downscaling factor. - * (= The denominator member in the sh_css_bds_factor structure.) + * @param[out] bds: The rational fraction of the bayer downscaling factor. + * (= The respective member in the sh_css_bds_factor structure.) * @return 0 or error code upon error. * */ -int sh_css_bds_factor_get_numerator_denominator( - unsigned int bds_factor, - unsigned int *bds_factor_numerator, - unsigned int *bds_factor_denominator); +int sh_css_bds_factor_get_fract(unsigned int bds_factor, struct u32_fract *bds); /* @brief Get a binary descriptor for preview stage. * diff --git a/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_stagedesc.h b/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_stagedesc.h index 40c8145a0797..7a0c988d89ee 100644 --- a/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_stagedesc.h +++ b/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_stagedesc.h @@ -38,11 +38,6 @@ void ia_css_pipe_get_firmwares_stage_desc( const struct ia_css_fw_info *fw, unsigned int mode); -void ia_css_pipe_get_acc_stage_desc( - struct ia_css_pipeline_stage_desc *stage_desc, - struct ia_css_binary *binary, - struct ia_css_fw_info *fw); - void ia_css_pipe_get_sp_func_stage_desc( struct ia_css_pipeline_stage_desc *stage_desc, struct ia_css_frame *out_frame, diff --git a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_binarydesc.c b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_binarydesc.c index 7dd0e4a53c8b..06664ce75b60 100644 --- a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_binarydesc.c +++ b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_binarydesc.c @@ -13,6 +13,9 @@ * more details. */ +#include <linux/kernel.h> +#include <linux/math.h> + #include "ia_css_pipe_binarydesc.h" #include "ia_css_frame_format.h" #include "ia_css_pipe.h" @@ -23,7 +26,6 @@ #include <assert_support.h> /* HRT_GDC_N */ #include "gdc_device.h" -#include <linux/kernel.h> /* This module provides a binary descriptions to used to find a binary. Since, * every stage is associated with a binary, it implicity helps stage @@ -126,40 +128,29 @@ void ia_css_pipe_get_vfpp_binarydesc( IA_CSS_LEAVE_PRIVATE(""); } -static struct sh_css_bds_factor bds_factors_list[] = { - {1, 1, SH_CSS_BDS_FACTOR_1_00}, - {5, 4, SH_CSS_BDS_FACTOR_1_25}, - {3, 2, SH_CSS_BDS_FACTOR_1_50}, - {2, 1, SH_CSS_BDS_FACTOR_2_00}, - {9, 4, SH_CSS_BDS_FACTOR_2_25}, - {5, 2, SH_CSS_BDS_FACTOR_2_50}, - {3, 1, SH_CSS_BDS_FACTOR_3_00}, - {4, 1, SH_CSS_BDS_FACTOR_4_00}, - {9, 2, SH_CSS_BDS_FACTOR_4_50}, - {5, 1, SH_CSS_BDS_FACTOR_5_00}, - {6, 1, SH_CSS_BDS_FACTOR_6_00}, - {8, 1, SH_CSS_BDS_FACTOR_8_00} +static struct u32_fract bds_factors_list[] = { + [SH_CSS_BDS_FACTOR_1_00] = {1, 1}, + [SH_CSS_BDS_FACTOR_1_25] = {5, 4}, + [SH_CSS_BDS_FACTOR_1_50] = {3, 2}, + [SH_CSS_BDS_FACTOR_2_00] = {2, 1}, + [SH_CSS_BDS_FACTOR_2_25] = {9, 4}, + [SH_CSS_BDS_FACTOR_2_50] = {5, 2}, + [SH_CSS_BDS_FACTOR_3_00] = {3, 1}, + [SH_CSS_BDS_FACTOR_4_00] = {4, 1}, + [SH_CSS_BDS_FACTOR_4_50] = {9, 2}, + [SH_CSS_BDS_FACTOR_5_00] = {5, 1}, + [SH_CSS_BDS_FACTOR_6_00] = {6, 1}, + [SH_CSS_BDS_FACTOR_8_00] = {8, 1}, }; -int sh_css_bds_factor_get_numerator_denominator( - unsigned int bds_factor, - unsigned int *bds_factor_numerator, - unsigned int *bds_factor_denominator) +int sh_css_bds_factor_get_fract(unsigned int bds_factor, struct u32_fract *bds) { - unsigned int i; - - /* Loop over all bds factors until a match is found */ - for (i = 0; i < ARRAY_SIZE(bds_factors_list); i++) { - if (bds_factors_list[i].bds_factor == bds_factor) { - *bds_factor_numerator = bds_factors_list[i].numerator; - *bds_factor_denominator = bds_factors_list[i].denominator; - return 0; - } - } + /* Throw an error since bds_factor cannot be found in bds_factors_list */ + if (bds_factor >= ARRAY_SIZE(bds_factors_list)) + return -EINVAL; - /* Throw an error since bds_factor cannot be found - in bds_factors_list */ - return -EINVAL; + *bds = bds_factors_list[bds_factor]; + return 0; } int binarydesc_calculate_bds_factor( @@ -194,7 +185,7 @@ int binarydesc_calculate_bds_factor( (out_h * num / den <= in_h); if (cond) { - *bds_factor = bds_factors_list[i].bds_factor; + *bds_factor = i; return 0; } } diff --git a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c index 82a24aabe8ce..6c93fa1c683b 100644 --- a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c +++ b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c @@ -74,27 +74,6 @@ void ia_css_pipe_get_firmwares_stage_desc( stage_desc->vf_frame = vf_frame; } -void ia_css_pipe_get_acc_stage_desc( - struct ia_css_pipeline_stage_desc *stage_desc, - struct ia_css_binary *binary, - struct ia_css_fw_info *fw) -{ - unsigned int i; - - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, - "ia_css_pipe_get_acc_stage_desc() enter:\n"); - stage_desc->binary = binary; - stage_desc->firmware = fw; - stage_desc->sp_func = IA_CSS_PIPELINE_NO_FUNC; - stage_desc->max_input_width = 0; - stage_desc->mode = IA_CSS_BINARY_MODE_VF_PP; - stage_desc->in_frame = NULL; - for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { - stage_desc->out_frame[i] = NULL; - } - stage_desc->vf_frame = NULL; -} - void ia_css_pipe_get_sp_func_stage_desc( struct ia_css_pipeline_stage_desc *stage_desc, struct ia_css_frame *out_frame, diff --git a/drivers/staging/media/atomisp/pci/css_2401_system/host/pixelgen_private.h b/drivers/staging/media/atomisp/pci/css_2401_system/host/pixelgen_private.h index 1c7938d8ccb5..8f79424bedb2 100644 --- a/drivers/staging/media/atomisp/pci/css_2401_system/host/pixelgen_private.h +++ b/drivers/staging/media/atomisp/pci/css_2401_system/host/pixelgen_private.h @@ -161,7 +161,7 @@ STORAGE_CLASS_PIXELGEN_C void pixelgen_ctrl_dump_state( state->syng_stat_fcnt); ia_css_print("Pixel Generator ID %d syng stat done 0x%x\n", ID, state->syng_stat_done); - ia_css_print("Pixel Generator ID %d tpg modee 0x%x\n", ID, state->tpg_mode); + ia_css_print("Pixel Generator ID %d tpg mode 0x%x\n", ID, state->tpg_mode); ia_css_print("Pixel Generator ID %d tpg hcnt mask 0x%x\n", ID, state->tpg_hcnt_mask); ia_css_print("Pixel Generator ID %d tpg hcnt mask 0x%x\n", ID, diff --git a/drivers/staging/media/atomisp/pci/hmm/hmm.c b/drivers/staging/media/atomisp/pci/hmm/hmm.c index fc6cfe9f7744..bb12644fd033 100644 --- a/drivers/staging/media/atomisp/pci/hmm/hmm.c +++ b/drivers/staging/media/atomisp/pci/hmm/hmm.c @@ -41,11 +41,9 @@ static bool hmm_initialized; /* * p: private - * s: shared - * u: user - * i: ion + * v: vmalloc */ -static const char hmm_bo_type_string[] = "psui"; +static const char hmm_bo_type_string[] = "pv"; static ssize_t bo_show(struct device *dev, struct device_attribute *attr, char *buf, struct list_head *bo_list, bool active) @@ -168,7 +166,8 @@ void hmm_cleanup(void) hmm_initialized = false; } -static ia_css_ptr __hmm_alloc(size_t bytes, enum hmm_bo_type type, const void __user *userptr) +static ia_css_ptr __hmm_alloc(size_t bytes, enum hmm_bo_type type, + void *vmalloc_addr) { unsigned int pgnr; struct hmm_buffer_object *bo; @@ -192,7 +191,7 @@ static ia_css_ptr __hmm_alloc(size_t bytes, enum hmm_bo_type type, const void __ } /* Allocate pages for memory */ - ret = hmm_bo_alloc_pages(bo, type, userptr); + ret = hmm_bo_alloc_pages(bo, type, vmalloc_addr); if (ret) { dev_err(atomisp_dev, "hmm_bo_alloc_pages failed.\n"); goto alloc_page_err; @@ -205,9 +204,8 @@ static ia_css_ptr __hmm_alloc(size_t bytes, enum hmm_bo_type type, const void __ goto bind_err; } - dev_dbg(atomisp_dev, - "%s: pages: 0x%08x (%zu bytes), type: %d, user ptr %p\n", - __func__, bo->start, bytes, type, userptr); + dev_dbg(atomisp_dev, "pages: 0x%08x (%zu bytes), type: %d, vmalloc %p\n", + bo->start, bytes, type, vmalloc); return bo->start; @@ -224,9 +222,9 @@ ia_css_ptr hmm_alloc(size_t bytes) return __hmm_alloc(bytes, HMM_BO_PRIVATE, NULL); } -ia_css_ptr hmm_create_from_userdata(size_t bytes, const void __user *userptr) +ia_css_ptr hmm_create_from_vmalloc_buf(size_t bytes, void *vmalloc_addr) { - return __hmm_alloc(bytes, HMM_BO_USER, userptr); + return __hmm_alloc(bytes, HMM_BO_VMALLOC, vmalloc_addr); } void hmm_free(ia_css_ptr virt) diff --git a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c index a5fd6d38d3c4..5e53eed8ae95 100644 --- a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c +++ b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c @@ -638,6 +638,7 @@ static int alloc_private_pages(struct hmm_buffer_object *bo) ret = alloc_pages_bulk_array(gfp, bo->pgnr, bo->pages); if (ret != bo->pgnr) { free_pages_bulk_array(ret, bo->pages); + dev_err(atomisp_dev, "alloc_pages_bulk_array() failed\n"); return -ENOMEM; } @@ -651,61 +652,34 @@ static int alloc_private_pages(struct hmm_buffer_object *bo) return 0; } -static void free_user_pages(struct hmm_buffer_object *bo, - unsigned int page_nr) +static int alloc_vmalloc_pages(struct hmm_buffer_object *bo, void *vmalloc_addr) { + void *vaddr = vmalloc_addr; int i; - for (i = 0; i < page_nr; i++) - put_page(bo->pages[i]); -} - -/* - * Convert user space virtual address into pages list - */ -static int alloc_user_pages(struct hmm_buffer_object *bo, - const void __user *userptr) -{ - int page_nr; - - userptr = untagged_addr(userptr); - - /* Handle frame buffer allocated in user space */ - mutex_unlock(&bo->mutex); - page_nr = get_user_pages_fast((unsigned long)userptr, bo->pgnr, 1, bo->pages); - mutex_lock(&bo->mutex); - - /* can be written by caller, not forced */ - if (page_nr != bo->pgnr) { - dev_err(atomisp_dev, - "get_user_pages err: bo->pgnr = %d, pgnr actually pinned = %d.\n", - bo->pgnr, page_nr); - if (page_nr < 0) - page_nr = 0; - goto out_of_mem; + for (i = 0; i < bo->pgnr; i++) { + bo->pages[i] = vmalloc_to_page(vaddr); + if (!bo->pages[i]) { + dev_err(atomisp_dev, "Error could not get page %d of vmalloc buf\n", i); + return -ENOMEM; + } + vaddr += PAGE_SIZE; } return 0; - -out_of_mem: - - free_user_pages(bo, page_nr); - - return -ENOMEM; } /* * allocate/free physical pages for the bo. * * type indicate where are the pages from. currently we have 3 types - * of memory: HMM_BO_PRIVATE, HMM_BO_USER. + * of memory: HMM_BO_PRIVATE, HMM_BO_VMALLOC. * - * userptr is only valid when type is HMM_BO_USER, it indicates - * the start address from user space task. + * vmalloc_addr is only valid when type is HMM_BO_VMALLOC. */ int hmm_bo_alloc_pages(struct hmm_buffer_object *bo, enum hmm_bo_type type, - const void __user *userptr) + void *vmalloc_addr) { int ret = -EINVAL; @@ -720,14 +694,10 @@ int hmm_bo_alloc_pages(struct hmm_buffer_object *bo, goto alloc_err; } - /* - * TO DO: - * add HMM_BO_USER type - */ if (type == HMM_BO_PRIVATE) { ret = alloc_private_pages(bo); - } else if (type == HMM_BO_USER) { - ret = alloc_user_pages(bo, userptr); + } else if (type == HMM_BO_VMALLOC) { + ret = alloc_vmalloc_pages(bo, vmalloc_addr); } else { dev_err(atomisp_dev, "invalid buffer type.\n"); ret = -EINVAL; @@ -771,8 +741,8 @@ void hmm_bo_free_pages(struct hmm_buffer_object *bo) if (bo->type == HMM_BO_PRIVATE) free_private_bo_pages(bo); - else if (bo->type == HMM_BO_USER) - free_user_pages(bo, bo->pgnr); + else if (bo->type == HMM_BO_VMALLOC) + ; /* No-op, nothing to do */ else dev_err(atomisp_dev, "invalid buffer type.\n"); diff --git a/drivers/staging/media/atomisp/pci/ia_css_frame_public.h b/drivers/staging/media/atomisp/pci/ia_css_frame_public.h index 514d933f934d..7ba464abf447 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_frame_public.h +++ b/drivers/staging/media/atomisp/pci/ia_css_frame_public.h @@ -20,6 +20,7 @@ * This file contains structs to describe various frame-formats supported by the ISP. */ +#include <media/videobuf2-v4l2.h> #include <type_support.h> #include "ia_css_err.h" #include "ia_css_types.h" @@ -146,7 +147,18 @@ enum ia_css_frame_flash_state { * This is the main structure used for all input and output images. */ struct ia_css_frame { - struct ia_css_frame_info info; /** info struct describing the frame */ + /* + * The videobuf2 core will allocate buffers including room for private + * data (the rest of struct ia_css_frame). The vb2_v4l2_buffer must + * be the first member for this to work! + * Note the atomisp code also uses ia_css_frame-s which are not used + * as v4l2-buffers in some places. In this case the vb2 member will + * be unused. + */ + struct vb2_v4l2_buffer vb; + /* List-head for linking into the activeq or buffers_waiting_for_param list */ + struct list_head queue; + struct ia_css_frame_info frame_info; /** info struct describing the frame */ ia_css_ptr data; /** pointer to start of image data */ unsigned int data_bytes; /** size of image data in bytes */ /* LA: move this to ia_css_buffer */ @@ -183,22 +195,16 @@ struct ia_css_frame { info.format */ }; +#define vb_to_frame(vb2) \ + container_of(to_vb2_v4l2_buffer(vb2), struct ia_css_frame, vb) + #define DEFAULT_FRAME { \ - .info = IA_CSS_BINARY_DEFAULT_FRAME_INFO, \ + .frame_info = IA_CSS_BINARY_DEFAULT_FRAME_INFO, \ .dynamic_queue_id = SH_CSS_INVALID_QUEUE_ID, \ .buf_type = IA_CSS_BUFFER_TYPE_INVALID, \ .flash_state = IA_CSS_FRAME_FLASH_STATE_NONE, \ } -/* @brief Fill a frame with zeros - * - * @param frame The frame. - * @return None - * - * Fill a frame with pixel values of zero - */ -void ia_css_frame_zero(struct ia_css_frame *frame); - /* @brief Allocate a CSS frame structure * * @param frame The allocated frame. @@ -220,6 +226,17 @@ ia_css_frame_allocate(struct ia_css_frame **frame, unsigned int stride, unsigned int raw_bit_depth); +/* @brief Initialize a CSS frame structure using a frame info structure. + * + * @param frame The allocated frame. + * @param[in] info The frame info structure. + * @return The error code. + * + * Initialize a frame using the resolution and format from a frame info struct. + */ +int ia_css_frame_init_from_info(struct ia_css_frame *frame, + const struct ia_css_frame_info *info); + /* @brief Allocate a CSS frame structure using a frame info structure. * * @param frame The allocated frame. @@ -244,69 +261,10 @@ ia_css_frame_allocate_from_info(struct ia_css_frame **frame, void ia_css_frame_free(struct ia_css_frame *frame); -/* @brief Allocate a CSS frame structure using a frame info structure. - * - * @param frame The allocated frame. - * @param[in] info The frame info structure. - * @return The error code. - * - * Allocate an empty CSS frame with no data buffer using the parameters - * in the frame info. - */ -int -ia_css_frame_create_from_info(struct ia_css_frame **frame, - const struct ia_css_frame_info *info); - -/* @brief Set a mapped data buffer to a CSS frame - * - * @param[in] frame Valid CSS frame pointer - * @param[in] mapped_data Mapped data buffer to be assigned to the CSS frame - * @param[in] data_size_bytes Size of the mapped_data in bytes - * @return The error code. - * - * Sets a mapped data buffer to this frame. This function can be called multiple - * times with different buffers or NULL to reset the data pointer. This API - * would not try free the mapped_data and its the callers responsiblity to - * free the mapped_data buffer. However if ia_css_frame_free() is called and - * the frame had a valid data buffer, it would be freed along with the frame. - */ -int -ia_css_frame_set_data(struct ia_css_frame *frame, - const ia_css_ptr mapped_data, - size_t data_size_bytes); - -/* @brief Map an existing frame data pointer to a CSS frame. - * - * @param frame Pointer to the frame to be initialized - * @param[in] info The frame info. - * @param[in] data Pointer to the allocated frame data. - * @param[in] attribute Attributes to be passed to mmgr_mmap. - * @param[in] context Pointer to the a context to be passed to mmgr_mmap. - * @return The allocated frame structure. - * - * This function maps a pre-allocated pointer into a CSS frame. This can be - * used when an upper software layer is responsible for allocating the frame - * data and it wants to share that frame pointer with the CSS code. - * This function will fill the CSS frame structure just like - * ia_css_frame_allocate() does, but instead of allocating the memory, it will - * map the pre-allocated memory into the CSS address space. - */ -int -ia_css_frame_map(struct ia_css_frame **frame, - const struct ia_css_frame_info *info, - const void __user *data, - unsigned int pgnr); - -/* @brief Unmap a CSS frame structure. - * - * @param[in] frame Pointer to the CSS frame. - * @return None - * - * This function unmaps the frame data pointer within a CSS frame and - * then frees the CSS frame structure. Use this for frame pointers created - * using ia_css_frame_map(). - */ -void -ia_css_frame_unmap(struct ia_css_frame *frame); +static inline const struct ia_css_frame_info * +ia_css_frame_get_info(const struct ia_css_frame *frame) +{ + return frame ? &frame->frame_info : NULL; +} #endif /* __IA_CSS_FRAME_PUBLIC_H */ diff --git a/drivers/staging/media/atomisp/pci/ia_css_pipe.h b/drivers/staging/media/atomisp/pci/ia_css_pipe.h index fb58535bff40..22522968b9e6 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_pipe.h +++ b/drivers/staging/media/atomisp/pci/ia_css_pipe.h @@ -37,7 +37,6 @@ struct ia_css_preview_settings { struct ia_css_pipe *copy_pipe; struct ia_css_pipe *capture_pipe; - struct ia_css_pipe *acc_pipe; }; #define IA_CSS_DEFAULT_PREVIEW_SETTINGS { \ @@ -156,7 +155,7 @@ struct ia_css_pipe { #define IA_CSS_DEFAULT_PIPE { \ .config = DEFAULT_PIPE_CONFIG, \ .info = DEFAULT_PIPE_INFO, \ - .mode = IA_CSS_PIPE_ID_ACC, /* (pipe_id) */ \ + .mode = IA_CSS_PIPE_ID_VIDEO, /* (pipe_id) */ \ .pipeline = DEFAULT_PIPELINE, \ .output_info = {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, \ .bds_output_info = IA_CSS_BINARY_DEFAULT_FRAME_INFO, \ diff --git a/drivers/staging/media/atomisp/pci/ia_css_pipe_public.h b/drivers/staging/media/atomisp/pci/ia_css_pipe_public.h index 7352cbf779fb..8ac1586dce4e 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_pipe_public.h +++ b/drivers/staging/media/atomisp/pci/ia_css_pipe_public.h @@ -45,7 +45,6 @@ enum ia_css_pipe_mode { IA_CSS_PIPE_MODE_PREVIEW, /** Preview pipe */ IA_CSS_PIPE_MODE_VIDEO, /** Video pipe */ IA_CSS_PIPE_MODE_CAPTURE, /** Still capture pipe */ - IA_CSS_PIPE_MODE_ACC, /** Accelerated pipe */ IA_CSS_PIPE_MODE_COPY, /** Copy pipe, only used for embedded/image data copying */ IA_CSS_PIPE_MODE_YUVPP, /** YUV post processing pipe, used for all use cases with YUV input, for SoC sensor and external ISP */ @@ -95,21 +94,11 @@ struct ia_css_pipe_config { /** output of YUV scaling */ struct ia_css_frame_info vf_output_info[IA_CSS_PIPE_MAX_OUTPUT_STAGE]; /** output of VF YUV scaling */ - struct ia_css_fw_info *acc_extension; - /** Pipeline extension accelerator */ - struct ia_css_fw_info **acc_stages; - /** Standalone accelerator stages */ - u32 num_acc_stages; - /** Number of standalone accelerator stages */ struct ia_css_capture_config default_capture_config; /** Default capture config for initial capture pipe configuration. */ struct ia_css_resolution dvs_envelope; /** temporary */ enum ia_css_frame_delay dvs_frame_delay; /** indicates the DVS loop delay in frame periods */ - int acc_num_execs; - /** For acceleration pipes only: determine how many times the pipe - should be run. Setting this to -1 means it will run until - stopped. */ bool enable_dz; /** Disabling digital zoom for a pipeline, if this is set to false, then setting a zoom factor will have no effect. @@ -153,7 +142,6 @@ struct ia_css_pipe_config { .vf_output_info = {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, \ .default_capture_config = DEFAULT_CAPTURE_CONFIG, \ .dvs_frame_delay = IA_CSS_FRAME_DELAY_1, \ - .acc_num_execs = -1, \ } /* Pipe info, this struct describes properties of a pipe after it's stream has @@ -224,9 +212,6 @@ struct ia_css_pipe_info { {{0, 0}, 0, 0, 0, 0}, // second_output_info {{0, 0}, 0, 0, 0, 0}, // vf_output_info {{0, 0}, 0, 0, 0, 0}, // second_vf_output_info - NULL, // acc_extension - NULL, // acc_stages - 0, // num_acc_stages { IA_CSS_CAPTURE_MODE_RAW, // mode false, // enable_xnr @@ -234,7 +219,6 @@ struct ia_css_pipe_info { }, // default_capture_config {0, 0}, // dvs_envelope 1, // dvs_frame_delay - -1, // acc_num_execs true, // enable_dz NULL, // p_isp_config }; @@ -426,59 +410,6 @@ int ia_css_pipe_dequeue_buffer(struct ia_css_pipe *pipe, struct ia_css_buffer *buffer); -/* @brief Set the state (Enable or Disable) of the Extension stage in the - * given pipe. - * @param[in] pipe Pipe handle. - * @param[in] fw_handle Extension firmware Handle (ia_css_fw_info.handle) - * @param[in] enable Enable Flag (1 to enable ; 0 to disable) - * - * @return - * 0 : Success - * -EINVAL : Invalid Parameters - * -EBUSY : Inactive QOS Pipe - * (No active stream with this pipe) - * - * This function will request state change (enable or disable) for the Extension - * stage (firmware handle) in the given pipe. - * - * Note: - * 1. Extension can be enabled/disabled only on QOS Extensions - * 2. Extension can be enabled/disabled only with an active QOS Pipe - * 3. Initial(Default) state of QOS Extensions is Disabled - * 4. State change cannot be guaranteed immediately OR on frame boundary - * - */ -int -ia_css_pipe_set_qos_ext_state(struct ia_css_pipe *pipe, - u32 fw_handle, - bool enable); - -/* @brief Get the state (Enable or Disable) of the Extension stage in the - * given pipe. - * @param[in] pipe Pipe handle. - * @param[in] fw_handle Extension firmware Handle (ia_css_fw_info.handle) - * @param[out] *enable Enable Flag - * - * @return - * 0 : Success - * -EINVAL : Invalid Parameters - * -EBUSY : Inactive QOS Pipe - * (No active stream with this pipe) - * - * This function will query the state of the Extension stage (firmware handle) - * in the given Pipe. - * - * Note: - * 1. Extension state can be queried only on QOS Extensions - * 2. Extension can be enabled/disabled only with an active QOS Pipe - * 3. Initial(Default) state of QOS Extensions is Disabled. - * - */ -int -ia_css_pipe_get_qos_ext_state(struct ia_css_pipe *pipe, - u32 fw_handle, - bool *enable); - /* @brief Get selected configuration settings * @param[in] pipe The pipe. * @param[out] config Configuration settings. diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io.host.c index c7d88552dfde..0091e2a3da52 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io.host.c @@ -28,9 +28,7 @@ int ia_css_bayer_io_config(const struct ia_css_binary *binary, const struct ia_css_frame *in_frame = args->in_frame; const struct ia_css_frame **out_frames = (const struct ia_css_frame **) &args->out_frame; - const struct ia_css_frame_info *in_frame_info = (in_frame) ? &in_frame->info : - &binary->in_frame_info; - + const struct ia_css_frame_info *in_frame_info = ia_css_frame_get_info(in_frame); const unsigned int ddr_bits_per_element = sizeof(short) * 8; const unsigned int ddr_elems_per_word = ceil_div(HIVE_ISP_DDR_WORD_BITS, ddr_bits_per_element); @@ -80,12 +78,12 @@ int ia_css_bayer_io_config(const struct ia_css_binary *binary, "ia_css_bayer_io_config() put part enter:\n"); #endif - ret = ia_css_dma_configure_from_info(&config, &out_frames[0]->info); + ret = ia_css_dma_configure_from_info(&config, &out_frames[0]->frame_info); if (ret) return ret; to->base_address = out_frames[0]->data; - to->width = out_frames[0]->info.res.width; - to->height = out_frames[0]->info.res.height; + to->width = out_frames[0]->frame_info.res.width; + to->height = out_frames[0]->frame_info.res.height; to->stride = config.stride; to->ddr_elems_per_word = ddr_elems_per_word; diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.c index 7d2ef6e26ee6..32c504a950ce 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.c @@ -28,9 +28,7 @@ int ia_css_yuv444_io_config(const struct ia_css_binary *binary, const struct ia_css_frame *in_frame = args->in_frame; const struct ia_css_frame **out_frames = (const struct ia_css_frame **) &args->out_frame; - const struct ia_css_frame_info *in_frame_info = (in_frame) ? &in_frame->info : - &binary->in_frame_info; - + const struct ia_css_frame_info *in_frame_info = ia_css_frame_get_info(in_frame); const unsigned int ddr_bits_per_element = sizeof(short) * 8; const unsigned int ddr_elems_per_word = ceil_div(HIVE_ISP_DDR_WORD_BITS, ddr_bits_per_element); @@ -81,13 +79,13 @@ int ia_css_yuv444_io_config(const struct ia_css_binary *binary, "ia_css_yuv444_io_config() put part enter:\n"); #endif - ret = ia_css_dma_configure_from_info(&config, &out_frames[0]->info); + ret = ia_css_dma_configure_from_info(&config, &out_frames[0]->frame_info); if (ret) return ret; to->base_address = out_frames[0]->data; - to->width = out_frames[0]->info.res.width; - to->height = out_frames[0]->info.res.height; + to->width = out_frames[0]->frame_info.res.width; + to->height = out_frames[0]->frame_info.res.height; to->stride = config.stride; to->ddr_elems_per_word = ddr_elems_per_word; diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/ref/ref_1.0/ia_css_ref.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/ref/ref_1.0/ia_css_ref.host.c index 08ed916a7eb8..9288a7a37b37 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/ref/ref_1.0/ia_css_ref.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/ref/ref_1.0/ia_css_ref.host.c @@ -30,7 +30,7 @@ int ia_css_ref_config(struct sh_css_isp_ref_isp_config *to, int ret; if (from->ref_frames[0]) { - ret = ia_css_dma_configure_from_info(&to->port_b, &from->ref_frames[0]->info); + ret = ia_css_dma_configure_from_info(&to->port_b, &from->ref_frames[0]->frame_info); if (ret) return ret; to->width_a_over_b = elems_a / to->port_b.elems; diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c index 53050c0c49fc..a5fea753ec64 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c @@ -79,11 +79,11 @@ int ia_css_tnr_config(struct sh_css_isp_tnr_isp_config *to, unsigned int i; int ret; - ret = ia_css_dma_configure_from_info(&to->port_b, &from->tnr_frames[0]->info); + ret = ia_css_dma_configure_from_info(&to->port_b, &from->tnr_frames[0]->frame_info); if (ret) return ret; to->width_a_over_b = elems_a / to->port_b.elems; - to->frame_height = from->tnr_frames[0]->info.res.height; + to->frame_height = from->tnr_frames[0]->frame_info.res.height; for (i = 0; i < NUM_VIDEO_TNR_FRAMES; i++) { to->tnr_frame_addr[i] = from->tnr_frames[i]->data + from->tnr_frames[i]->planes.yuyv.offset; diff --git a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c index 406ed5fb4c6a..768da86b8c2c 100644 --- a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c +++ b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c @@ -13,6 +13,8 @@ * more details. */ +#include <linux/math.h> + #include <math_support.h> #include <gdc_device.h> /* HR_GDC_N */ @@ -128,16 +130,8 @@ ia_css_binary_compute_shading_table_bayer_origin( { int err; - /* Numerator and denominator of the fixed bayer downscaling factor. - (numerator >= denominator) */ - unsigned int bds_num, bds_den; - - /* Horizontal/Vertical ratio of bayer scaling - between input area and output area. */ - unsigned int bs_hor_ratio_in; - unsigned int bs_hor_ratio_out; - unsigned int bs_ver_ratio_in; - unsigned int bs_ver_ratio_out; + /* Rational fraction of the fixed bayer downscaling factor. */ + struct u32_fract bds; /* Left padding set by InputFormatter. */ unsigned int left_padding_bqs; /* in bqs */ @@ -158,19 +152,11 @@ ia_css_binary_compute_shading_table_bayer_origin( unsigned int bad_bqs_on_top_before_bs; /* in bqs */ unsigned int bad_bqs_on_top_after_bs; /* in bqs */ - /* Get the numerator and denominator of bayer downscaling factor. */ - err = sh_css_bds_factor_get_numerator_denominator - (required_bds_factor, &bds_num, &bds_den); + /* Get the rational fraction of bayer downscaling factor. */ + err = sh_css_bds_factor_get_fract(required_bds_factor, &bds); if (err) return err; - /* Set the horizontal/vertical ratio of bayer scaling - between input area and output area. */ - bs_hor_ratio_in = bds_num; - bs_hor_ratio_out = bds_den; - bs_ver_ratio_in = bds_num; - bs_ver_ratio_out = bds_den; - /* Set the left padding set by InputFormatter. (ifmtr.c) */ if (stream_config->left_padding == -1) left_padding_bqs = _ISP_BQS(binary->left_padding); @@ -228,18 +214,18 @@ ia_css_binary_compute_shading_table_bayer_origin( located on the shading table during the shading correction. */ res->sc_bayer_origin_x_bqs_on_shading_table = ((left_padding_adjusted_bqs + bad_bqs_on_left_before_bs) - * bs_hor_ratio_out + bs_hor_ratio_in / 2) / bs_hor_ratio_in + * bds.denominator + bds.numerator / 2) / bds.numerator + bad_bqs_on_left_after_bs; - /* "+ bs_hor_ratio_in/2": rounding for division by bs_hor_ratio_in */ + /* "+ bds.numerator / 2": rounding for division by bds.numerator */ res->sc_bayer_origin_y_bqs_on_shading_table = - (bad_bqs_on_top_before_bs * bs_ver_ratio_out + bs_ver_ratio_in / 2) / bs_ver_ratio_in + (bad_bqs_on_top_before_bs * bds.denominator + bds.numerator / 2) / bds.numerator + bad_bqs_on_top_after_bs; - /* "+ bs_ver_ratio_in/2": rounding for division by bs_ver_ratio_in */ + /* "+ bds.numerator / 2": rounding for division by bds.numerator */ - res->bayer_scale_hor_ratio_in = (uint32_t)bs_hor_ratio_in; - res->bayer_scale_hor_ratio_out = (uint32_t)bs_hor_ratio_out; - res->bayer_scale_ver_ratio_in = (uint32_t)bs_ver_ratio_in; - res->bayer_scale_ver_ratio_out = (uint32_t)bs_ver_ratio_out; + res->bayer_scale_hor_ratio_in = bds.numerator; + res->bayer_scale_hor_ratio_out = bds.denominator; + res->bayer_scale_ver_ratio_in = bds.numerator; + res->bayer_scale_ver_ratio_out = bds.denominator; return err; } diff --git a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c index 3d269bd23207..bb6204cb42c5 100644 --- a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c +++ b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c @@ -133,7 +133,6 @@ static const char *const pipe_id_to_str[] = { /* [IA_CSS_PIPE_ID_VIDEO] =*/ "video", /* [IA_CSS_PIPE_ID_CAPTURE] =*/ "capture", /* [IA_CSS_PIPE_ID_YUVPP] =*/ "yuvpp", - /* [IA_CSS_PIPE_ID_ACC] =*/ "accelerator" }; static char dot_id_input_bin[SH_CSS_MAX_BINARY_NAME + 10]; @@ -1301,11 +1300,11 @@ void ia_css_debug_frame_print(const struct ia_css_frame *frame, data = (char *)HOST_ADDRESS(frame->data); ia_css_debug_dtrace(2, "frame %s (%p):\n", descr, frame); ia_css_debug_dtrace(2, " resolution = %dx%d\n", - frame->info.res.width, frame->info.res.height); + frame->frame_info.res.width, frame->frame_info.res.height); ia_css_debug_dtrace(2, " padded width = %d\n", - frame->info.padded_width); - ia_css_debug_dtrace(2, " format = %d\n", frame->info.format); - switch (frame->info.format) { + frame->frame_info.padded_width); + ia_css_debug_dtrace(2, " format = %d\n", frame->frame_info.format); + switch (frame->frame_info.format) { case IA_CSS_FRAME_FORMAT_NV12: case IA_CSS_FRAME_FORMAT_NV16: case IA_CSS_FRAME_FORMAT_NV21: @@ -2565,11 +2564,11 @@ ia_css_debug_pipe_graph_dump_frame( dtrace_dot( "node [shape = box, fixedsize=true, width=2, height=0.7]; \"%p\" [label = \"%s\\n%d(%d) x %d, %dbpp\\n%s\"];", frame, - debug_frame_format2str(frame->info.format), - frame->info.res.width, - frame->info.padded_width, - frame->info.res.height, - frame->info.raw_bit_depth, + debug_frame_format2str(frame->frame_info.format), + frame->frame_info.res.width, + frame->frame_info.padded_width, + frame->frame_info.res.height, + frame->frame_info.raw_bit_depth, bufinfo); if (in_frame) { @@ -2866,10 +2865,10 @@ ia_css_debug_pipe_graph_dump_sp_raw_copy( snprintf(ring_buffer, sizeof(ring_buffer), "node [shape = box, fixedsize=true, width=2, height=0.7]; \"%p\" [label = \"%s\\n%d(%d) x %d\\nRingbuffer\"];", out_frame, - debug_frame_format2str(out_frame->info.format), - out_frame->info.res.width, - out_frame->info.padded_width, - out_frame->info.res.height); + debug_frame_format2str(out_frame->frame_info.format), + out_frame->frame_info.res.width, + out_frame->frame_info.padded_width, + out_frame->frame_info.res.height); dtrace_dot(ring_buffer); @@ -2989,16 +2988,10 @@ ia_css_debug_dump_pipe_config( ia_css_debug_dump_frame_info(&config->vf_output_info[i], "vf_output_info"); } - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "acc_extension: %p\n", - config->acc_extension); - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "num_acc_stages: %d\n", - config->num_acc_stages); ia_css_debug_dump_capture_config(&config->default_capture_config); ia_css_debug_dump_resolution(&config->dvs_envelope, "dvs_envelope"); ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "dvs_frame_delay: %d\n", config->dvs_frame_delay); - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "acc_num_execs: %d\n", - config->acc_num_execs); ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "enable_dz: %d\n", config->enable_dz); IA_CSS_LEAVE_PRIVATE(""); diff --git a/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c b/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c index 5a7058320ee6..83bb42e05421 100644 --- a/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c +++ b/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c @@ -88,12 +88,6 @@ ia_css_elems_bytes_from_info( ** CSS API functions, exposed by ia_css.h **************************************************************************/ -void ia_css_frame_zero(struct ia_css_frame *frame) -{ - assert(frame); - hmm_set(frame->data, 0, frame->data_bytes); -} - int ia_css_frame_allocate_from_info(struct ia_css_frame **frame, const struct ia_css_frame_info *info) { @@ -143,121 +137,6 @@ int ia_css_frame_allocate(struct ia_css_frame **frame, return err; } -int ia_css_frame_map(struct ia_css_frame **frame, - const struct ia_css_frame_info *info, - const void __user *data, - unsigned int pgnr) -{ - int err = 0; - struct ia_css_frame *me; - - assert(frame); - - /* Create the frame structure */ - err = ia_css_frame_create_from_info(&me, info); - - if (err) - return err; - - if (pgnr < ((PAGE_ALIGN(me->data_bytes)) >> PAGE_SHIFT)) { - dev_err(atomisp_dev, - "user space memory size is less than the expected size..\n"); - err = -ENOMEM; - goto error; - } else if (pgnr > ((PAGE_ALIGN(me->data_bytes)) >> PAGE_SHIFT)) { - dev_err(atomisp_dev, - "user space memory size is large than the expected size..\n"); - err = -ENOMEM; - goto error; - } - - me->data = hmm_create_from_userdata(me->data_bytes, data); - if (me->data == mmgr_NULL) - err = -EINVAL; - -error: - if (err) { - kvfree(me); - me = NULL; - } - - *frame = me; - - return err; -} - -int ia_css_frame_create_from_info(struct ia_css_frame **frame, - const struct ia_css_frame_info *info) -{ - int err = 0; - struct ia_css_frame *me; - - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_frame_create_from_info() enter:\n"); - if (!frame || !info) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_frame_create_from_info() leave: invalid arguments\n"); - return -EINVAL; - } - - me = frame_create(info->res.width, - info->res.height, - info->format, - info->padded_width, - info->raw_bit_depth, - false); - if (!me) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_frame_create_from_info() leave: frame create failed\n"); - return -ENOMEM; - } - - err = ia_css_frame_init_planes(me); - - if (err) { - kvfree(me); - me = NULL; - } - - *frame = me; - - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_frame_create_from_info() leave:\n"); - - return err; -} - -int ia_css_frame_set_data(struct ia_css_frame *frame, - const ia_css_ptr mapped_data, - size_t data_bytes) -{ - int err = 0; - - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_frame_set_data() enter:\n"); - if (!frame) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_frame_set_data() leave: NULL frame\n"); - return -EINVAL; - } - - /* If we are setting a valid data. - * Make sure that there is enough - * room for the expected frame format - */ - if ((mapped_data != mmgr_NULL) && (frame->data_bytes > data_bytes)) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_frame_set_data() leave: invalid arguments\n"); - return -EINVAL; - } - - frame->data = mapped_data; - - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "ia_css_frame_set_data() leave:\n"); - - return err; -} - void ia_css_frame_free(struct ia_css_frame *frame) { IA_CSS_ENTER_PRIVATE("frame = %p", frame); @@ -286,32 +165,32 @@ int ia_css_frame_init_planes(struct ia_css_frame *frame) { assert(frame); - switch (frame->info.format) { + switch (frame->frame_info.format) { case IA_CSS_FRAME_FORMAT_MIPI: dev_err(atomisp_dev, "%s: unexpected use of IA_CSS_FRAME_FORMAT_MIPI\n", __func__); return -EINVAL; case IA_CSS_FRAME_FORMAT_RAW_PACKED: frame_init_raw_single_plane(frame, &frame->planes.raw, - frame->info.res.height, - frame->info.padded_width, - frame->info.raw_bit_depth); + frame->frame_info.res.height, + frame->frame_info.padded_width, + frame->frame_info.raw_bit_depth); break; case IA_CSS_FRAME_FORMAT_RAW: frame_init_single_plane(frame, &frame->planes.raw, - frame->info.res.height, - frame->info.padded_width, - frame->info.raw_bit_depth <= 8 ? 1 : 2); + frame->frame_info.res.height, + frame->frame_info.padded_width, + frame->frame_info.raw_bit_depth <= 8 ? 1 : 2); break; case IA_CSS_FRAME_FORMAT_RGB565: frame_init_single_plane(frame, &frame->planes.rgb, - frame->info.res.height, - frame->info.padded_width, 2); + frame->frame_info.res.height, + frame->frame_info.padded_width, 2); break; case IA_CSS_FRAME_FORMAT_RGBA888: frame_init_single_plane(frame, &frame->planes.rgb, - frame->info.res.height, - frame->info.padded_width * 4, 1); + frame->frame_info.res.height, + frame->frame_info.padded_width * 4, 1); break; case IA_CSS_FRAME_FORMAT_PLANAR_RGB888: frame_init_rgb_planes(frame, 1); @@ -324,14 +203,14 @@ int ia_css_frame_init_planes(struct ia_css_frame *frame) case IA_CSS_FRAME_FORMAT_CSI_MIPI_YUV420_8: case IA_CSS_FRAME_FORMAT_CSI_MIPI_LEGACY_YUV420_8: frame_init_single_plane(frame, &frame->planes.yuyv, - frame->info.res.height, - frame->info.padded_width * 2, 1); + frame->frame_info.res.height, + frame->frame_info.padded_width * 2, 1); break; case IA_CSS_FRAME_FORMAT_YUV_LINE: /* Needs 3 extra lines to allow vf_pp prefetching */ frame_init_single_plane(frame, &frame->planes.yuyv, - frame->info.res.height * 3 / 2 + 3, - frame->info.padded_width, 1); + frame->frame_info.res.height * 3 / 2 + 3, + frame->frame_info.padded_width, 1); break; case IA_CSS_FRAME_FORMAT_NV11: frame_init_nv_planes(frame, 4, 1, 1); @@ -380,8 +259,8 @@ int ia_css_frame_init_planes(struct ia_css_frame *frame) break; case IA_CSS_FRAME_FORMAT_BINARY_8: frame_init_single_plane(frame, &frame->planes.binary.data, - frame->info.res.height, - frame->info.padded_width, 1); + frame->frame_info.res.height, + frame->frame_info.padded_width, 1); frame->planes.binary.size = 0; break; default: @@ -510,8 +389,8 @@ bool ia_css_frame_is_same_type(const struct ia_css_frame *frame_a, const struct ia_css_frame *frame_b) { bool is_equal = false; - const struct ia_css_frame_info *info_a = &frame_a->info, - *info_b = &frame_b->info; + const struct ia_css_frame_info *info_a = &frame_a->frame_info; + const struct ia_css_frame_info *info_b = &frame_b->frame_info; ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "ia_css_frame_is_same_type() enter:\n"); @@ -613,8 +492,8 @@ static void frame_init_nv_planes(struct ia_css_frame *frame, unsigned int vertical_decimation, unsigned int bytes_per_element) { - unsigned int y_width = frame->info.padded_width; - unsigned int y_height = frame->info.res.height; + unsigned int y_width = frame->frame_info.padded_width; + unsigned int y_height = frame->frame_info.res.height; unsigned int uv_width; unsigned int uv_height; unsigned int y_bytes; @@ -627,7 +506,7 @@ static void frame_init_nv_planes(struct ia_css_frame *frame, uv_width = 2 * (y_width / horizontal_decimation); uv_height = y_height / vertical_decimation; - if (frame->info.format == IA_CSS_FRAME_FORMAT_NV12_TILEY) { + if (frame->frame_info.format == IA_CSS_FRAME_FORMAT_NV12_TILEY) { y_width = CEIL_MUL(y_width, NV12_TILEY_TILE_WIDTH); uv_width = CEIL_MUL(uv_width, NV12_TILEY_TILE_WIDTH); y_height = CEIL_MUL(y_height, NV12_TILEY_TILE_HEIGHT); @@ -652,8 +531,8 @@ static void frame_init_yuv_planes(struct ia_css_frame *frame, bool swap_uv, unsigned int bytes_per_element) { - unsigned int y_width = frame->info.padded_width, - y_height = frame->info.res.height, + unsigned int y_width = frame->frame_info.padded_width, + y_height = frame->frame_info.res.height, uv_width = y_width / horizontal_decimation, uv_height = y_height / vertical_decimation, y_stride, y_bytes, uv_bytes, uv_stride; @@ -682,8 +561,8 @@ static void frame_init_yuv_planes(struct ia_css_frame *frame, static void frame_init_rgb_planes(struct ia_css_frame *frame, unsigned int bytes_per_element) { - unsigned int width = frame->info.res.width, - height = frame->info.res.height, stride, bytes; + unsigned int width = frame->frame_info.res.width, + height = frame->frame_info.res.height, stride, bytes; stride = width * bytes_per_element; bytes = stride * height; @@ -698,8 +577,8 @@ static void frame_init_rgb_planes(struct ia_css_frame *frame, static void frame_init_qplane6_planes(struct ia_css_frame *frame) { - unsigned int width = frame->info.padded_width / 2, - height = frame->info.res.height / 2, bytes, stride; + unsigned int width = frame->frame_info.padded_width / 2, + height = frame->frame_info.res.height / 2, bytes, stride; stride = width * 2; bytes = stride * height; @@ -781,11 +660,11 @@ static struct ia_css_frame *frame_create(unsigned int width, return NULL; memset(me, 0, sizeof(*me)); - me->info.res.width = width; - me->info.res.height = height; - me->info.format = format; - me->info.padded_width = padded_width; - me->info.raw_bit_depth = raw_bit_depth; + me->frame_info.res.width = width; + me->frame_info.res.height = height; + me->frame_info.format = format; + me->frame_info.padded_width = padded_width; + me->frame_info.raw_bit_depth = raw_bit_depth; me->valid = valid; me->data_bytes = 0; me->data = mmgr_NULL; @@ -847,3 +726,19 @@ void ia_css_resolution_to_sp_resolution( to->width = (uint16_t)from->width; to->height = (uint16_t)from->height; } + +int ia_css_frame_init_from_info(struct ia_css_frame *frame, + const struct ia_css_frame_info *frame_info) +{ + frame->frame_info.res.width = frame_info->res.width; + frame->frame_info.res.height = frame_info->res.height; + frame->frame_info.format = frame_info->format; + frame->frame_info.padded_width = frame_info->padded_width; + frame->frame_info.raw_bit_depth = frame_info->raw_bit_depth; + frame->valid = true; + /* To indicate it is not valid frame. */ + frame->dynamic_queue_id = SH_CSS_INVALID_QUEUE_ID; + frame->buf_type = IA_CSS_BUFFER_TYPE_INVALID; + + return ia_css_frame_init_planes(frame); +} diff --git a/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h b/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h index de2c526a58ae..222c381ff3b9 100644 --- a/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h +++ b/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h @@ -54,7 +54,6 @@ struct ia_css_pipeline { unsigned int inout_port_config; int num_execs; bool acquire_isp_each_stage; - u32 pipe_qos_config; }; #define DEFAULT_PIPELINE { \ @@ -65,7 +64,6 @@ struct ia_css_pipeline { .dvs_frame_delay = IA_CSS_FRAME_DELAY_1, \ .num_execs = -1, \ .acquire_isp_each_stage = true, \ - .pipe_qos_config = QOS_INVALID \ } /* Stage descriptor used to create a new stage in the pipeline */ diff --git a/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c b/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c index dfc50247ea8e..e9e187649a65 100644 --- a/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c +++ b/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c @@ -774,14 +774,6 @@ ia_css_pipeline_configure_inout_port(struct ia_css_pipeline *me, (uint8_t)SH_CSS_PORT_OUTPUT, (uint8_t)SH_CSS_HOST_TYPE, 1); break; - case IA_CSS_PIPE_ID_ACC: - SH_CSS_PIPE_PORT_CONFIG_SET(me->inout_port_config, - (uint8_t)SH_CSS_PORT_INPUT, - (uint8_t)SH_CSS_HOST_TYPE, 1); - SH_CSS_PIPE_PORT_CONFIG_SET(me->inout_port_config, - (uint8_t)SH_CSS_PORT_OUTPUT, - (uint8_t)SH_CSS_HOST_TYPE, 1); - break; default: break; } diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index da96aaffebc1..726cb7aa4ecd 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -209,13 +209,6 @@ ia_css_pipe_check_format(struct ia_css_pipe *pipe, enum ia_css_frame_format format); /* ISP 2401 */ -static int -ia_css_pipe_load_extension(struct ia_css_pipe *pipe, - struct ia_css_fw_info *firmware); - -static void -ia_css_pipe_unload_extension(struct ia_css_pipe *pipe, - struct ia_css_fw_info *firmware); static void ia_css_reset_defaults(struct sh_css *css); @@ -287,10 +280,6 @@ init_out_frameinfo_defaults(struct ia_css_pipe *pipe, struct ia_css_frame *out_frame, unsigned int idx); static int -sh_css_pipeline_add_acc_stage(struct ia_css_pipeline *pipeline, - const void *acc_fw); - -static int alloc_continuous_frames(struct ia_css_pipe *pipe, bool init_time); static void @@ -329,9 +318,6 @@ create_host_capture_pipeline(struct ia_css_pipe *pipe); static int create_host_yuvpp_pipeline(struct ia_css_pipe *pipe); -static int -create_host_acc_pipeline(struct ia_css_pipe *pipe); - static unsigned int sh_css_get_sw_interrupt_value(unsigned int irq); @@ -362,12 +348,6 @@ static struct sh_css_hmm_buffer_record *sh_css_hmm_buffer_record_validate(ia_css_ptr ddr_buffer_addr, enum ia_css_buffer_type type); -void -ia_css_get_acc_configs( - struct ia_css_pipe *pipe, - struct ia_css_isp_config *config); - - #ifdef ISP2401 static unsigned int get_crop_lines_for_bayer_order(const struct ia_css_stream_config *config); @@ -1649,15 +1629,6 @@ ia_css_enable_isys_event_queue(bool enable) return 0; } -/* For Acceleration API: Flush FW (shared buffer pointer) arguments */ -void -sh_css_flush(struct ia_css_acc_fw *fw) -{ - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "sh_css_flush() enter:\n"); - if ((fw) && (my_css.flush)) - my_css.flush(fw); -} - /* * Mapping sp threads. Currently, this is done when a stream is created and * pipelines are ready to be converted to sp pipelines. Be careful if you are @@ -1670,7 +1641,6 @@ map_sp_threads(struct ia_css_stream *stream, bool map) struct ia_css_pipe *main_pipe = NULL; struct ia_css_pipe *copy_pipe = NULL; struct ia_css_pipe *capture_pipe = NULL; - struct ia_css_pipe *acc_pipe = NULL; int err = 0; enum ia_css_pipe_id pipe_id; @@ -1691,7 +1661,6 @@ map_sp_threads(struct ia_css_stream *stream, bool map) case IA_CSS_PIPE_ID_PREVIEW: copy_pipe = main_pipe->pipe_settings.preview.copy_pipe; capture_pipe = main_pipe->pipe_settings.preview.capture_pipe; - acc_pipe = main_pipe->pipe_settings.preview.acc_pipe; break; case IA_CSS_PIPE_ID_VIDEO: @@ -1700,14 +1669,10 @@ map_sp_threads(struct ia_css_stream *stream, bool map) break; case IA_CSS_PIPE_ID_CAPTURE: - case IA_CSS_PIPE_ID_ACC: default: break; } - if (acc_pipe) - ia_css_pipeline_map(acc_pipe->pipe_num, map); - if (capture_pipe) ia_css_pipeline_map(capture_pipe->pipe_num, map); @@ -1735,7 +1700,6 @@ static int create_host_pipeline_structure(struct ia_css_stream *stream) { struct ia_css_pipe *copy_pipe = NULL, *capture_pipe = NULL; - struct ia_css_pipe *acc_pipe = NULL; enum ia_css_pipe_id pipe_id; struct ia_css_pipe *main_pipe = NULL; int err = 0; @@ -1763,7 +1727,6 @@ create_host_pipeline_structure(struct ia_css_stream *stream) copy_pipe_delay = main_pipe->dvs_frame_delay; capture_pipe = main_pipe->pipe_settings.preview.capture_pipe; capture_pipe_delay = IA_CSS_FRAME_DELAY_0; - acc_pipe = main_pipe->pipe_settings.preview.acc_pipe; err = ia_css_pipeline_create(&main_pipe->pipeline, main_pipe->mode, main_pipe->pipe_num, main_pipe->dvs_frame_delay); break; @@ -1787,11 +1750,6 @@ create_host_pipeline_structure(struct ia_css_stream *stream) main_pipe->pipe_num, main_pipe->dvs_frame_delay); break; - case IA_CSS_PIPE_ID_ACC: - err = ia_css_pipeline_create(&main_pipe->pipeline, main_pipe->mode, - main_pipe->pipe_num, main_pipe->dvs_frame_delay); - break; - default: err = -EINVAL; } @@ -1808,10 +1766,6 @@ create_host_pipeline_structure(struct ia_css_stream *stream) capture_pipe->pipe_num, capture_pipe_delay); - if (!(err) && acc_pipe) - err = ia_css_pipeline_create(&acc_pipe->pipeline, acc_pipe->mode, - acc_pipe->pipe_num, main_pipe->dvs_frame_delay); - /* DH regular multi pipe - not continuous mode: create the next pipelines too */ if (!stream->config.continuous) { int i; @@ -1837,7 +1791,6 @@ static int create_host_pipeline(struct ia_css_stream *stream) { struct ia_css_pipe *copy_pipe = NULL, *capture_pipe = NULL; - struct ia_css_pipe *acc_pipe = NULL; enum ia_css_pipe_id pipe_id; struct ia_css_pipe *main_pipe = NULL; int err = 0; @@ -1881,27 +1834,17 @@ create_host_pipeline(struct ia_css_stream *stream) } } -#if !defined(ISP2401) /* old isys: need to allocate_mipi_frames() even in IA_CSS_PIPE_MODE_COPY */ - if (pipe_id != IA_CSS_PIPE_ID_ACC) { - err = allocate_mipi_frames(main_pipe, &stream->info); - if (err) - goto ERR; - } -#elif defined(ISP2401) - if ((pipe_id != IA_CSS_PIPE_ID_ACC) && - (main_pipe->config.mode != IA_CSS_PIPE_MODE_COPY)) { + if (!IS_ISP2401 || main_pipe->config.mode != IA_CSS_PIPE_MODE_COPY) { err = allocate_mipi_frames(main_pipe, &stream->info); if (err) goto ERR; } -#endif switch (pipe_id) { case IA_CSS_PIPE_ID_PREVIEW: copy_pipe = main_pipe->pipe_settings.preview.copy_pipe; capture_pipe = main_pipe->pipe_settings.preview.capture_pipe; - acc_pipe = main_pipe->pipe_settings.preview.acc_pipe; max_input_width = main_pipe->pipe_settings.preview.preview_binary.info->sp.input.max_width; @@ -1935,12 +1878,6 @@ create_host_pipeline(struct ia_css_stream *stream) break; - case IA_CSS_PIPE_ID_ACC: - err = create_host_acc_pipeline(main_pipe); - if (err) - goto ERR; - - break; default: err = -EINVAL; } @@ -1960,12 +1897,6 @@ create_host_pipeline(struct ia_css_stream *stream) goto ERR; } - if (acc_pipe) { - err = create_host_acc_pipeline(acc_pipe); - if (err) - goto ERR; - } - /* DH regular multi pipe - not continuous mode: create the next pipelines too */ if (!stream->config.continuous) { int i; @@ -1984,9 +1915,6 @@ create_host_pipeline(struct ia_css_stream *stream) case IA_CSS_PIPE_ID_YUVPP: err = create_host_yuvpp_pipeline(stream->pipes[i]); break; - case IA_CSS_PIPE_ID_ACC: - err = create_host_acc_pipeline(stream->pipes[i]); - break; default: err = -EINVAL; } @@ -2037,9 +1965,6 @@ init_pipe_defaults(enum ia_css_pipe_mode mode, pipe->mode = IA_CSS_PIPE_ID_VIDEO; memcpy(&pipe->pipe_settings.video, &video, sizeof(video)); break; - case IA_CSS_PIPE_MODE_ACC: - pipe->mode = IA_CSS_PIPE_ID_ACC; - break; case IA_CSS_PIPE_MODE_COPY: pipe->mode = IA_CSS_PIPE_ID_CAPTURE; break; @@ -2156,27 +2081,6 @@ find_pipe_by_num(uint32_t pipe_num) return NULL; } -static void sh_css_pipe_free_acc_binaries( - struct ia_css_pipe *pipe) -{ - struct ia_css_pipeline *pipeline; - struct ia_css_pipeline_stage *stage; - - if (!pipe) { - IA_CSS_ERROR("NULL input pointer"); - return; - } - pipeline = &pipe->pipeline; - - /* loop through the stages and unload them */ - for (stage = pipeline->stages; stage; stage = stage->next) { - struct ia_css_fw_info *firmware = (struct ia_css_fw_info *) - stage->firmware; - if (firmware) - ia_css_pipe_unload_extension(pipe, firmware); - } -} - int ia_css_pipe_destroy(struct ia_css_pipe *pipe) { @@ -2241,9 +2145,6 @@ ia_css_pipe_destroy(struct ia_css_pipe *pipe) ia_css_frame_free_multiple(MAX_NUM_VIDEO_DELAY_FRAMES, pipe->pipe_settings.capture.delay_frames); break; - case IA_CSS_PIPE_MODE_ACC: - sh_css_pipe_free_acc_binaries(pipe); - break; case IA_CSS_PIPE_MODE_COPY: break; case IA_CSS_PIPE_MODE_YUVPP: @@ -2261,10 +2162,6 @@ ia_css_pipe_destroy(struct ia_css_pipe *pipe) ia_css_pipeline_destroy(&pipe->pipeline); pipe_release_pipe_num(ia_css_pipe_get_pipe_num(pipe)); - /* Temporarily, not every sh_css_pipe has an acc_extension. */ - if (pipe->config.acc_extension) - ia_css_pipe_unload_extension(pipe, pipe->config.acc_extension); - kfree(pipe); IA_CSS_LEAVE("err = %d", err); return err; @@ -3060,7 +2957,7 @@ init_vf_frameinfo_defaults(struct ia_css_pipe *pipe, assert(vf_frame); - sh_css_pipe_get_viewfinder_frame_info(pipe, &vf_frame->info, idx); + sh_css_pipe_get_viewfinder_frame_info(pipe, &vf_frame->frame_info, idx); vf_frame->flash_state = IA_CSS_FRAME_FLASH_STATE_NONE; ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id); ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME + idx, thread_id, &queue_id); @@ -3229,31 +3126,31 @@ init_in_frameinfo_memory_defaults(struct ia_css_pipe *pipe, assert(frame); in_frame = frame; - in_frame->info.format = format; + in_frame->frame_info.format = format; #ifdef ISP2401 if (format == IA_CSS_FRAME_FORMAT_RAW) - in_frame->info.format = (pipe->stream->config.pack_raw_pixels) ? + in_frame->frame_info.format = (pipe->stream->config.pack_raw_pixels) ? IA_CSS_FRAME_FORMAT_RAW_PACKED : IA_CSS_FRAME_FORMAT_RAW; #endif - in_frame->info.res.width = pipe->stream->config.input_config.input_res.width; - in_frame->info.res.height = pipe->stream->config.input_config.input_res.height; - in_frame->info.raw_bit_depth = - ia_css_pipe_util_pipe_input_format_bpp(pipe); - ia_css_frame_info_set_width(&in_frame->info, pipe->stream->config.input_config.input_res.width, 0); + in_frame->frame_info.res.width = pipe->stream->config.input_config.input_res.width; + in_frame->frame_info.res.height = pipe->stream->config.input_config.input_res.height; + in_frame->frame_info.raw_bit_depth = ia_css_pipe_util_pipe_input_format_bpp(pipe); + ia_css_frame_info_set_width(&in_frame->frame_info, + pipe->stream->config.input_config.input_res.width, 0); in_frame->flash_state = IA_CSS_FRAME_FLASH_STATE_NONE; ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id); ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_INPUT_FRAME, thread_id, &queue_id); in_frame->dynamic_queue_id = queue_id; in_frame->buf_type = IA_CSS_BUFFER_TYPE_INPUT_FRAME; #ifdef ISP2401 - ia_css_get_crop_offsets(pipe, &in_frame->info); + ia_css_get_crop_offsets(pipe, &in_frame->frame_info); #endif err = ia_css_frame_init_planes(in_frame); - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, - "init_in_frameinfo_memory_defaults() bayer_order = %d:\n", in_frame->info.raw_bayer_order); + ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "%s() bayer_order = %d\n", + __func__, in_frame->frame_info.raw_bayer_order); return err; } @@ -3268,7 +3165,7 @@ init_out_frameinfo_defaults(struct ia_css_pipe *pipe, assert(out_frame); - sh_css_pipe_get_output_frame_info(pipe, &out_frame->info, idx); + sh_css_pipe_get_output_frame_info(pipe, &out_frame->frame_info, idx); out_frame->flash_state = IA_CSS_FRAME_FLASH_STATE_NONE; ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id); ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_OUTPUT_FRAME + idx, thread_id, &queue_id); @@ -3433,31 +3330,6 @@ static int create_host_video_pipeline(struct ia_css_pipe *pipe) } } - /* Append Extension on Video out, if enabled */ - if (!need_vf_pp && video_stage && pipe->config.acc_extension && - (pipe->config.acc_extension->info.isp.type == IA_CSS_ACC_OUTPUT)) { - struct ia_css_frame *out = NULL; - struct ia_css_frame *in = NULL; - - if ((pipe->config.acc_extension->info.isp.sp.enable.output) && - (pipe->config.acc_extension->info.isp.sp.enable.in_frame) && - (pipe->config.acc_extension->info.isp.sp.enable.out_frame)) { - /* In/Out Frame mapping to support output frame extension.*/ - out = video_stage->args.out_frame[0]; - err = ia_css_frame_allocate_from_info(&in, &pipe->output_info[0]); - if (err) - goto ERR; - video_stage->args.out_frame[0] = in; - } - - err = add_firmwares(me, video_binary, pipe->output_stage, - last_output_firmware(pipe->output_stage), - IA_CSS_BINARY_MODE_VIDEO, - in, out, NULL, &video_stage, NULL); - if (err) - goto ERR; - } - if (need_yuv_pp && video_stage) { struct ia_css_frame *tmp_in_frame = video_stage->args.out_frame[0]; struct ia_css_frame *tmp_out_frame = NULL; @@ -3489,45 +3361,6 @@ ERR: return err; } -static int -create_host_acc_pipeline(struct ia_css_pipe *pipe) -{ - int err = 0; - const struct ia_css_fw_info *fw; - unsigned int i; - - IA_CSS_ENTER_PRIVATE("pipe = %p", pipe); - if ((!pipe) || (!pipe->stream)) { - IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL); - return -EINVAL; - } - - pipe->pipeline.num_execs = pipe->config.acc_num_execs; - /* Reset pipe_qos_config to default disable all QOS extension stages */ - if (pipe->config.acc_extension) - pipe->pipeline.pipe_qos_config = 0; - - for (fw = pipe->vf_stage; fw; fw = fw->next) { - err = sh_css_pipeline_add_acc_stage(&pipe->pipeline, fw); - if (err) - goto ERR; - } - - for (i = 0; i < pipe->config.num_acc_stages; i++) { - struct ia_css_fw_info *fw = pipe->config.acc_stages[i]; - - err = sh_css_pipeline_add_acc_stage(&pipe->pipeline, fw); - if (err) - goto ERR; - } - - ia_css_pipeline_finalize_stages(&pipe->pipeline, pipe->stream->config.continuous); - -ERR: - IA_CSS_LEAVE_ERR_PRIVATE(err); - return err; -} - /* Create stages for preview */ static int create_host_preview_pipeline(struct ia_css_pipe *pipe) @@ -3690,7 +3523,6 @@ preview_start(struct ia_css_pipe *pipe) { int err = 0; struct ia_css_pipe *copy_pipe, *capture_pipe; - struct ia_css_pipe *acc_pipe; enum sh_css_pipe_config_override copy_ovrd; enum ia_css_input_mode preview_pipe_input_mode; unsigned int thread_id; @@ -3705,7 +3537,6 @@ preview_start(struct ia_css_pipe *pipe) copy_pipe = pipe->pipe_settings.preview.copy_pipe; capture_pipe = pipe->pipe_settings.preview.capture_pipe; - acc_pipe = pipe->pipe_settings.preview.acc_pipe; sh_css_metrics_start_frame(); @@ -3764,22 +3595,6 @@ preview_start(struct ia_css_pipe *pipe) (enum mipi_port_id)0); } - if (acc_pipe) { - sh_css_sp_init_pipeline(&acc_pipe->pipeline, - IA_CSS_PIPE_ID_ACC, - (uint8_t)ia_css_pipe_get_pipe_num(acc_pipe), - false, - pipe->stream->config.pixels_per_clock == 2, - false, /* continuous */ - false, /* offline */ - pipe->required_bds_factor, - 0, - IA_CSS_INPUT_MODE_MEMORY, - NULL, - NULL, - (enum mipi_port_id)0); - } - start_pipe(pipe, copy_ovrd, preview_pipe_input_mode); IA_CSS_LEAVE_ERR_PRIVATE(err); @@ -3850,9 +3665,7 @@ ia_css_pipe_enqueue_buffer(struct ia_css_pipe *pipe, pipeline = &pipe->pipeline; - assert(pipeline || - pipe_id == IA_CSS_PIPE_ID_COPY || - pipe_id == IA_CSS_PIPE_ID_ACC); + assert(pipeline || pipe_id == IA_CSS_PIPE_ID_COPY); assert(sizeof(NULL) <= sizeof(ddr_buffer.kernel_ptr)); ddr_buffer.kernel_ptr = HOST_ADDRESS(NULL); @@ -4146,7 +3959,7 @@ ia_css_pipe_dequeue_buffer(struct ia_css_pipe *pipe, if (!frame->valid) pipe->num_invalid_frames--; - if (frame->info.format == IA_CSS_FRAME_FORMAT_BINARY_8) { + if (frame->frame_info.format == IA_CSS_FRAME_FORMAT_BINARY_8) { #ifdef ISP2401 frame->planes.binary.size = frame->data_bytes; #else @@ -4442,16 +4255,6 @@ ia_css_dequeue_isys_event(struct ia_css_event *event) return err; } -static void -acc_start(struct ia_css_pipe *pipe) -{ - assert(pipe); - assert(pipe->stream); - - start_pipe(pipe, SH_CSS_PIPE_CONFIG_OVRD_NO_OVRD, - pipe->stream->config.mode); -} - static int sh_css_pipe_start(struct ia_css_stream *stream) { @@ -4496,9 +4299,6 @@ sh_css_pipe_start(struct ia_css_stream *stream) case IA_CSS_PIPE_ID_YUVPP: err = yuvpp_start(pipe); break; - case IA_CSS_PIPE_ID_ACC: - acc_start(pipe); - break; default: err = -EINVAL; } @@ -4524,10 +4324,6 @@ sh_css_pipe_start(struct ia_css_stream *stream) stream->pipes[i]->stop_requested = false; err = yuvpp_start(stream->pipes[i]); break; - case IA_CSS_PIPE_ID_ACC: - stream->pipes[i]->stop_requested = false; - acc_start(stream->pipes[i]); - break; default: err = -EINVAL; } @@ -4620,22 +4416,6 @@ sh_css_pipe_start(struct ia_css_stream *stream) (uint8_t)thread_id, 0, 0); } - /* in case of PREVIEW mode, check whether QOS acc_pipe is available, then start the qos pipe */ - if (pipe_id == IA_CSS_PIPE_ID_PREVIEW) { - struct ia_css_pipe *acc_pipe = NULL; - - acc_pipe = pipe->pipe_settings.preview.acc_pipe; - - if (acc_pipe) { - ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(acc_pipe), - &thread_id); - /* by the time we reach here q is initialized and handle is available.*/ - ia_css_bufq_enqueue_psys_event( - IA_CSS_PSYS_SW_EVENT_START_STREAM, - (uint8_t)thread_id, 0, 0); - } - } - stream->started = true; IA_CSS_LEAVE_ERR_PRIVATE(err); @@ -6861,8 +6641,6 @@ sh_css_pipe_load_binaries(struct ia_css_pipe *pipe) case IA_CSS_PIPE_ID_YUVPP: err = load_yuvpp_binaries(pipe); break; - case IA_CSS_PIPE_ID_ACC: - break; default: err = -EINVAL; break; @@ -7102,7 +6880,7 @@ create_host_yuvpp_pipeline(struct ia_css_pipe *pipe) /* we use output port 1 as internal output port */ tmp_in_frame = yuv_scaler_stage->args.out_frame[1]; if (pipe->pipe_settings.yuvpp.is_output_stage[i]) { - if (tmp_vf_frame && (tmp_vf_frame->info.res.width != 0)) { + if (tmp_vf_frame && (tmp_vf_frame->frame_info.res.width != 0)) { in_frame = yuv_scaler_stage->args.out_vf_frame; err = add_vf_pp_stage(pipe, in_frame, tmp_vf_frame, @@ -7118,7 +6896,7 @@ create_host_yuvpp_pipeline(struct ia_css_pipe *pipe) } } } else if (copy_stage) { - if (vf_frame[0] && vf_frame[0]->info.res.width != 0) { + if (vf_frame[0] && vf_frame[0]->frame_info.res.width != 0) { in_frame = copy_stage->args.out_vf_frame; err = add_vf_pp_stage(pipe, in_frame, vf_frame[0], &vf_pp_binary[0], &vf_pp_stage); @@ -7158,10 +6936,10 @@ create_host_copy_pipeline(struct ia_css_pipe *pipe, if (copy_on_sp(pipe) && pipe->stream->config.input_config.format == ATOMISP_INPUT_FORMAT_BINARY_8) { - ia_css_frame_info_init(&out_frame->info, JPEG_BYTES, 1, + ia_css_frame_info_init(&out_frame->frame_info, JPEG_BYTES, 1, IA_CSS_FRAME_FORMAT_BINARY_8, 0); - } else if (out_frame->info.format == IA_CSS_FRAME_FORMAT_RAW) { - out_frame->info.raw_bit_depth = + } else if (out_frame->frame_info.format == IA_CSS_FRAME_FORMAT_RAW) { + out_frame->frame_info.raw_bit_depth = ia_css_pipe_util_pipe_input_format_bpp(pipe); } @@ -7200,7 +6978,7 @@ create_host_isyscopy_capture_pipeline(struct ia_css_pipe *pipe) ia_css_pipeline_clean(me); /* Construct out_frame info */ - err = sh_css_pipe_get_output_frame_info(pipe, &out_frame->info, 0); + err = sh_css_pipe_get_output_frame_info(pipe, &out_frame->frame_info, 0); if (err) return err; out_frame->flash_state = IA_CSS_FRAME_FLASH_STATE_NONE; @@ -7755,154 +7533,6 @@ ia_css_stream_end_input_frame(const struct ia_css_stream *stream) ia_css_inputfifo_end_frame(stream->config.channel_id); } -static void -append_firmware(struct ia_css_fw_info **l, struct ia_css_fw_info *firmware) -{ - IA_CSS_ENTER_PRIVATE("l = %p, firmware = %p", l, firmware); - if (!l) { - IA_CSS_ERROR("NULL fw_info"); - IA_CSS_LEAVE_PRIVATE(""); - return; - } - while (*l) - l = &(*l)->next; - *l = firmware; - /* when multiple acc extensions are loaded, 'next' can be not NULL */ - /*firmware->next = NULL;*/ - IA_CSS_LEAVE_PRIVATE(""); -} - -static void -remove_firmware(struct ia_css_fw_info **l, struct ia_css_fw_info *firmware) -{ - assert(*l); - assert(firmware); - (void)l; - (void)firmware; - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "remove_firmware() enter:\n"); - - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "remove_firmware() leave:\n"); - return; /* removing single and multiple firmware is handled in acc_unload_extension() */ -} - -static int upload_isp_code(struct ia_css_fw_info *firmware) -{ - ia_css_ptr binary; - - if (!firmware) { - IA_CSS_ERROR("NULL input parameter"); - return -EINVAL; - } - binary = firmware->info.isp.xmem_addr; - - if (!binary) { - unsigned int size = firmware->blob.size; - const unsigned char *blob; - const unsigned char *binary_name; - - binary_name = - (const unsigned char *)(IA_CSS_EXT_ISP_PROG_NAME( - firmware)); - blob = binary_name + - strlen((const char *)binary_name) + - 1; - binary = sh_css_load_blob(blob, size); - firmware->info.isp.xmem_addr = binary; - } - - if (!binary) - return -ENOMEM; - return 0; -} - -static int -acc_load_extension(struct ia_css_fw_info *firmware) -{ - int err; - struct ia_css_fw_info *hd = firmware; - - while (hd) { - err = upload_isp_code(hd); - if (err) - return err; - hd = hd->next; - } - - if (!firmware) - return -EINVAL; - firmware->loaded = true; - return 0; -} - -static void -acc_unload_extension(struct ia_css_fw_info *firmware) -{ - struct ia_css_fw_info *hd = firmware; - struct ia_css_fw_info *hdn = NULL; - - if (!firmware) /* should not happen */ - return; - /* unload and remove multiple firmwares */ - while (hd) { - hdn = (hd->next) ? &(*hd->next) : NULL; - if (hd->info.isp.xmem_addr) { - hmm_free(hd->info.isp.xmem_addr); - hd->info.isp.xmem_addr = mmgr_NULL; - } - hd->isp_code = NULL; - hd->next = NULL; - hd = hdn; - } - - firmware->loaded = false; -} - -/* Load firmware for extension */ -static int -ia_css_pipe_load_extension(struct ia_css_pipe *pipe, - struct ia_css_fw_info *firmware) -{ - int err = 0; - - IA_CSS_ENTER_PRIVATE("fw = %p pipe = %p", firmware, pipe); - - if ((!firmware) || (!pipe)) { - IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL); - return -EINVAL; - } - - if (firmware->info.isp.type == IA_CSS_ACC_OUTPUT) - append_firmware(&pipe->output_stage, firmware); - else if (firmware->info.isp.type == IA_CSS_ACC_VIEWFINDER) - append_firmware(&pipe->vf_stage, firmware); - err = acc_load_extension(firmware); - - IA_CSS_LEAVE_ERR_PRIVATE(err); - return err; -} - -/* Unload firmware for extension */ -static void -ia_css_pipe_unload_extension(struct ia_css_pipe *pipe, - struct ia_css_fw_info *firmware) -{ - IA_CSS_ENTER_PRIVATE("fw = %p pipe = %p", firmware, pipe); - - if ((!firmware) || (!pipe)) { - IA_CSS_ERROR("NULL input parameters"); - IA_CSS_LEAVE_PRIVATE(""); - return; - } - - if (firmware->info.isp.type == IA_CSS_ACC_OUTPUT) - remove_firmware(&pipe->output_stage, firmware); - else if (firmware->info.isp.type == IA_CSS_ACC_VIEWFINDER) - remove_firmware(&pipe->vf_stage, firmware); - acc_unload_extension(firmware); - - IA_CSS_LEAVE_PRIVATE(""); -} - bool ia_css_pipeline_uses_params(struct ia_css_pipeline *me) { @@ -7924,35 +7554,6 @@ ia_css_pipeline_uses_params(struct ia_css_pipeline *me) return false; } -static int -sh_css_pipeline_add_acc_stage(struct ia_css_pipeline *pipeline, - const void *acc_fw) -{ - struct ia_css_fw_info *fw = (struct ia_css_fw_info *)acc_fw; - /* In QoS case, load_extension already called, so skipping */ - int err = 0; - - if (!fw->loaded) - err = acc_load_extension(fw); - - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "sh_css_pipeline_add_acc_stage() enter: pipeline=%p, acc_fw=%p\n", - pipeline, acc_fw); - - if (!err) { - struct ia_css_pipeline_stage_desc stage_desc; - - ia_css_pipe_get_acc_stage_desc(&stage_desc, NULL, fw); - err = ia_css_pipeline_create_and_add_stage(pipeline, - &stage_desc, - NULL); - } - - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "sh_css_pipeline_add_acc_stage() leave: return_err=%d\n", err); - return err; -} - /* * @brief Tag a specific frame in continuous capture. * Refer to "sh_css_internal.h" for details. @@ -8177,26 +7778,6 @@ void ia_css_stream_config_defaults(struct ia_css_stream_config *stream_config) stream_config->source.port.rxcount = 0x04040404; } -static int -ia_css_acc_pipe_create(struct ia_css_pipe *pipe) -{ - int err = 0; - - if (!pipe) { - IA_CSS_ERROR("NULL input parameter"); - return -EINVAL; - } - - /* There is not meaning for num_execs = 0 semantically. Run at least once. */ - if (pipe->config.acc_num_execs == 0) - pipe->config.acc_num_execs = 1; - - if (pipe->config.acc_extension) - err = ia_css_pipe_load_extension(pipe, pipe->config.acc_extension); - - return err; -} - int ia_css_pipe_create(const struct ia_css_pipe_config *config, struct ia_css_pipe **pipe) { @@ -8257,23 +7838,6 @@ ia_css_pipe_create_extra(const struct ia_css_pipe_config *config, else ia_css_pipe_extra_config_defaults(&internal_pipe->extra_config); - if (config->mode == IA_CSS_PIPE_MODE_ACC) { - /* - * Temporary hack to migrate acceleration to CSS 2.0. - * In the future the code for all pipe types should be - * unified. - */ - *pipe = internal_pipe; - if (!internal_pipe->config.acc_extension && - internal_pipe->config.num_acc_stages == - 0) { /* if no acc binary and no standalone stage */ - *pipe = NULL; - IA_CSS_LEAVE_ERR_PRIVATE(0); - return 0; - } - return ia_css_acc_pipe_create(internal_pipe); - } - /* * Use config value when dvs_frame_delay setting equal to 2, * otherwise always 1 by default @@ -8368,15 +7932,6 @@ ia_css_pipe_create_extra(const struct ia_css_pipe_config *config, } } } - if (internal_pipe->config.acc_extension) { - err = ia_css_pipe_load_extension(internal_pipe, - internal_pipe->config.acc_extension); - if (err) { - IA_CSS_LEAVE_ERR_PRIVATE(err); - kvfree(internal_pipe); - return err; - } - } /* set all info to zeroes first */ memset(&internal_pipe->info, 0, sizeof(internal_pipe->info)); @@ -8525,57 +8080,6 @@ find_pipe(struct ia_css_pipe *pipes[], unsigned int num_pipes, } static int -ia_css_acc_stream_create(struct ia_css_stream *stream) -{ - int i; - int err = 0; - - IA_CSS_ENTER_PRIVATE("stream = %p", stream); - - if (!stream) { - IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL); - return -EINVAL; - } - - for (i = 0; i < stream->num_pipes; i++) { - struct ia_css_pipe *pipe = stream->pipes[i]; - - if (!pipe) { - IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL); - return -EINVAL; - } - - pipe->stream = stream; - } - - /* Map SP threads before doing anything. */ - err = map_sp_threads(stream, true); - if (err) { - IA_CSS_LEAVE_ERR_PRIVATE(err); - return err; - } - - for (i = 0; i < stream->num_pipes; i++) { - struct ia_css_pipe *pipe = stream->pipes[i]; - - assert(pipe); - ia_css_pipe_map_queue(pipe, true); - } - - err = create_host_pipeline_structure(stream); - if (err) { - IA_CSS_LEAVE_ERR_PRIVATE(err); - return err; - } - - stream->started = false; - - IA_CSS_LEAVE_ERR_PRIVATE(0); - - return 0; -} - -static int metadata_info_init(const struct ia_css_metadata_config *mdc, struct ia_css_metadata_info *md) { @@ -8807,11 +8311,6 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config, goto ERR; IA_CSS_LOG("isp_params_configs: %p", curr_stream->isp_params_configs); - if (num_pipes == 1 && pipes[0]->config.mode == IA_CSS_PIPE_MODE_ACC) { - *stream = curr_stream; - err = ia_css_acc_stream_create(curr_stream); - goto ERR; - } /* sensor binning */ if (!spcopyonly) { sensor_binning_changed = @@ -8832,7 +8331,6 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config, /* Search for the preview pipe and create the copy pipe */ struct ia_css_pipe *preview_pipe; struct ia_css_pipe *video_pipe; - struct ia_css_pipe *acc_pipe; struct ia_css_pipe *capture_pipe = NULL; struct ia_css_pipe *copy_pipe = NULL; @@ -8847,11 +8345,7 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config, IA_CSS_PIPE_MODE_PREVIEW, false); video_pipe = find_pipe(pipes, num_pipes, IA_CSS_PIPE_MODE_VIDEO, false); - acc_pipe = find_pipe(pipes, num_pipes, IA_CSS_PIPE_MODE_ACC, - false); - if (acc_pipe && num_pipes == 2 && curr_stream->cont_capt) - curr_stream->cont_capt = - false; /* preview + QoS case will not need cont_capt switch */ + if (curr_stream->cont_capt) { capture_pipe = find_pipe(pipes, num_pipes, IA_CSS_PIPE_MODE_CAPTURE, @@ -8888,9 +8382,6 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config, } if (video_pipe && curr_stream->cont_capt) video_pipe->pipe_settings.video.capture_pipe = capture_pipe; - - if (preview_pipe && acc_pipe) - preview_pipe->pipe_settings.preview.acc_pipe = acc_pipe; } for (i = 0; i < num_pipes; i++) { curr_pipe = pipes[i]; @@ -9738,13 +9229,6 @@ void ia_css_pipe_map_queue(struct ia_css_pipe *pipe, bool map) if (!pipe->stream->config.continuous) ia_css_queue_map(thread_id, IA_CSS_BUFFER_TYPE_OUTPUT_FRAME, map); ia_css_queue_map(thread_id, IA_CSS_BUFFER_TYPE_METADATA, map); - } else if (pipe->mode == IA_CSS_PIPE_ID_ACC) { - if (need_input_queue) - ia_css_queue_map(thread_id, IA_CSS_BUFFER_TYPE_INPUT_FRAME, map); - ia_css_queue_map(thread_id, IA_CSS_BUFFER_TYPE_OUTPUT_FRAME, map); - ia_css_queue_map(thread_id, IA_CSS_BUFFER_TYPE_PARAMETER_SET, map); - ia_css_queue_map(thread_id, IA_CSS_BUFFER_TYPE_PER_FRAME_PARAMETER_SET, map); - ia_css_queue_map(thread_id, IA_CSS_BUFFER_TYPE_METADATA, map); } else if (pipe->mode == IA_CSS_PIPE_ID_YUVPP) { unsigned int idx; @@ -9795,92 +9279,6 @@ ia_css_unlock_raw_frame(struct ia_css_stream *stream, uint32_t exp_id) return ret; } -/* - * @brief Set the state (Enable or Disable) of the Extension stage in the - * given pipe. - */ -int -ia_css_pipe_set_qos_ext_state(struct ia_css_pipe *pipe, uint32_t fw_handle, - bool enable) -{ - unsigned int thread_id; - struct ia_css_pipeline_stage *stage; - int err = 0; - - IA_CSS_ENTER(""); - - /* Parameter Check */ - if (!pipe || !pipe->stream) { - IA_CSS_ERROR("Invalid Pipe."); - err = -EINVAL; - } else if (!(pipe->config.acc_extension)) { - IA_CSS_ERROR("Invalid Pipe(No Extension Firmware)"); - err = -EINVAL; - } else if (!sh_css_sp_is_running()) { - IA_CSS_ERROR("Leaving: queue unavailable."); - err = -EBUSY; - } else { - /* Query the threadid and stage_num for the Extension firmware*/ - ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id); - err = ia_css_pipeline_get_stage_from_fw(&pipe->pipeline, fw_handle, &stage); - if (!err) { - /* Set the Extension State;. TODO: Add check for stage firmware.type (QOS)*/ - err = ia_css_bufq_enqueue_psys_event( - (uint8_t)IA_CSS_PSYS_SW_EVENT_STAGE_ENABLE_DISABLE, - (uint8_t)thread_id, - (uint8_t)stage->stage_num, - enable ? 1 : 0); - if (!err) { - if (enable) - SH_CSS_QOS_STAGE_ENABLE(&sh_css_sp_group.pipe[thread_id], stage->stage_num); - else - SH_CSS_QOS_STAGE_DISABLE(&sh_css_sp_group.pipe[thread_id], stage->stage_num); - } - } - } - IA_CSS_LEAVE("err:%d handle:%u enable:%d", err, fw_handle, enable); - return err; -} - -/* - * @brief Get the state (Enable or Disable) of the Extension stage in the - * given pipe. - */ -int -ia_css_pipe_get_qos_ext_state(struct ia_css_pipe *pipe, uint32_t fw_handle, - bool *enable) -{ - struct ia_css_pipeline_stage *stage; - unsigned int thread_id; - int err = 0; - - IA_CSS_ENTER(""); - - /* Parameter Check */ - if (!pipe || !pipe->stream) { - IA_CSS_ERROR("Invalid Pipe."); - err = -EINVAL; - } else if (!(pipe->config.acc_extension)) { - IA_CSS_ERROR("Invalid Pipe (No Extension Firmware)."); - err = -EINVAL; - } else if (!sh_css_sp_is_running()) { - IA_CSS_ERROR("Leaving: queue unavailable."); - err = -EBUSY; - } else { - /* Query the threadid and stage_num corresponding to the Extension firmware*/ - ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id); - err = ia_css_pipeline_get_stage_from_fw(&pipe->pipeline, fw_handle, &stage); - - if (!err) { - /* Get the Extension State */ - *enable = (SH_CSS_QOS_STAGE_IS_ENABLED(&sh_css_sp_group.pipe[thread_id], - stage->stage_num)) ? true : false; - } - } - IA_CSS_LEAVE("err:%d handle:%u enable:%d", err, fw_handle, *enable); - return err; -} - static void sh_css_hmm_buffer_record_init(void) { diff --git a/drivers/staging/media/atomisp/pci/sh_css_internal.h b/drivers/staging/media/atomisp/pci/sh_css_internal.h index 435b3cedd1c3..d98f1323441e 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_internal.h +++ b/drivers/staging/media/atomisp/pci/sh_css_internal.h @@ -488,16 +488,6 @@ ia_css_metadata_free_multiple(unsigned int num_bufs, /* Macro for handling pipe_qos_config */ #define QOS_INVALID (~0U) -#define QOS_ALL_STAGES_DISABLED (0U) -#define QOS_STAGE_MASK(num) (0x00000001 << num) -#define SH_CSS_IS_QOS_PIPE(pipe) ((pipe)->pipe_qos_config != QOS_INVALID) -#define SH_CSS_QOS_STAGE_ENABLE(pipe, num) ((pipe)->pipe_qos_config |= QOS_STAGE_MASK(num)) -#define SH_CSS_QOS_STAGE_DISABLE(pipe, num) ((pipe)->pipe_qos_config &= ~QOS_STAGE_MASK(num)) -#define SH_CSS_QOS_STAGE_IS_ENABLED(pipe, num) ((pipe)->pipe_qos_config & QOS_STAGE_MASK(num)) -#define SH_CSS_QOS_STAGE_IS_ALL_DISABLED(pipe) ((pipe)->pipe_qos_config == QOS_ALL_STAGES_DISABLED) -#define SH_CSS_QOS_MODE_PIPE_ADD(mode, pipe) ((mode) |= (0x1 << (pipe)->pipe_id)) -#define SH_CSS_QOS_MODE_PIPE_REMOVE(mode, pipe) ((mode) &= ~(0x1 << (pipe)->pipe_id)) -#define SH_CSS_IS_QOS_ONLY_MODE(mode) ((mode) == (0x1 << IA_CSS_PIPE_ID_ACC)) /* Information for a pipeline */ struct sh_css_sp_pipeline { @@ -907,9 +897,6 @@ sh_css_params_init(void); void sh_css_params_uninit(void); -/* For Acceleration API: Flush FW (shared buffer pointer) arguments */ -void sh_css_flush(struct ia_css_acc_fw *fw); - void sh_css_binary_args_reset(struct sh_css_binary_args *args); diff --git a/drivers/staging/media/atomisp/pci/sh_css_legacy.h b/drivers/staging/media/atomisp/pci/sh_css_legacy.h index 567c8d6dcc2c..cdf239b070a8 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_legacy.h +++ b/drivers/staging/media/atomisp/pci/sh_css_legacy.h @@ -32,7 +32,6 @@ enum ia_css_pipe_id { IA_CSS_PIPE_ID_VIDEO, IA_CSS_PIPE_ID_CAPTURE, IA_CSS_PIPE_ID_YUVPP, - IA_CSS_PIPE_ID_ACC, IA_CSS_PIPE_ID_NUM }; diff --git a/drivers/staging/media/atomisp/pci/sh_css_param_shading.c b/drivers/staging/media/atomisp/pci/sh_css_param_shading.c index 41a4c9162319..5b43cc656269 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_param_shading.c +++ b/drivers/staging/media/atomisp/pci/sh_css_param_shading.c @@ -13,6 +13,7 @@ * more details. */ +#include <linux/math.h> #include <linux/slab.h> #include <math_support.h> @@ -239,10 +240,9 @@ prepare_shading_table(const struct ia_css_shading_table *in_table, { unsigned int input_width, input_height, table_width, table_height, i; unsigned int left_padding, top_padding, left_cropping; - unsigned int bds_numerator, bds_denominator; - int right_padding; - struct ia_css_shading_table *result; + struct u32_fract bds; + int right_padding; assert(target_table); assert(binary); @@ -265,17 +265,16 @@ prepare_shading_table(const struct ia_css_shading_table *in_table, left_cropping = (binary->info->sp.pipeline.left_cropping == 0) ? binary->dvs_envelope.width : 2 * ISP_VEC_NELEMS; - sh_css_bds_factor_get_numerator_denominator - (bds_factor, &bds_numerator, &bds_denominator); + sh_css_bds_factor_get_fract(bds_factor, &bds); left_padding = (left_padding + binary->info->sp.pipeline.left_cropping) * - bds_numerator / bds_denominator - + bds.numerator / bds.denominator - binary->info->sp.pipeline.left_cropping; right_padding = (binary->internal_frame_info.res.width - - binary->effective_in_frame_res.width * bds_denominator / - bds_numerator - left_cropping) * bds_numerator / bds_denominator; - top_padding = binary->info->sp.pipeline.top_cropping * bds_numerator / - bds_denominator - + binary->effective_in_frame_res.width * bds.denominator / + bds.numerator - left_cropping) * bds.numerator / bds.denominator; + top_padding = binary->info->sp.pipeline.top_cropping * bds.numerator / + bds.denominator - binary->info->sp.pipeline.top_cropping; /* diff --git a/drivers/staging/media/atomisp/pci/sh_css_params.c b/drivers/staging/media/atomisp/pci/sh_css_params.c index 67915d76a87f..f08564f58242 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_params.c +++ b/drivers/staging/media/atomisp/pci/sh_css_params.c @@ -936,8 +936,8 @@ sh_css_set_black_frame(struct ia_css_stream *stream, assert(raw_black_frame); params = stream->isp_params_configs; - height = raw_black_frame->info.res.height; - width = raw_black_frame->info.padded_width; + height = raw_black_frame->frame_info.res.height; + width = raw_black_frame->frame_info.padded_width; ptr = raw_black_frame->data + raw_black_frame->planes.raw.offset; @@ -1187,16 +1187,15 @@ ia_css_process_zoom_and_motion( const struct sh_css_binary_args *args = &stage->args; const struct ia_css_frame_info *out_infos[IA_CSS_BINARY_MAX_OUTPUT_PORTS] = {NULL}; - if (args->out_frame[0]) - out_infos[0] = &args->out_frame[0]->info; + out_infos[0] = ia_css_frame_get_info(args->out_frame[0]); + info = &stage->firmware->info.isp; ia_css_binary_fill_info(info, false, false, ATOMISP_INPUT_FORMAT_RAW_10, - args->in_frame ? &args->in_frame->info : NULL, + ia_css_frame_get_info(args->in_frame), NULL, out_infos, - args->out_vf_frame ? &args->out_vf_frame->info - : NULL, + ia_css_frame_get_info(args->out_vf_frame), &tmp_binary, NULL, -1, true); @@ -3461,10 +3460,10 @@ sh_css_params_write_to_ddr_internal( if (stage->args.delay_frames[0]) { /*When delay frames are present(as in case of video), they are used for dvs. Configure DVS using those params*/ - dvs_in_frame_info = &stage->args.delay_frames[0]->info; + dvs_in_frame_info = &stage->args.delay_frames[0]->frame_info; } else { /*Otherwise, use input frame to configure DVS*/ - dvs_in_frame_info = &stage->args.in_frame->info; + dvs_in_frame_info = &stage->args.in_frame->frame_info; } /* Generate default DVS unity table on start up*/ diff --git a/drivers/staging/media/atomisp/pci/sh_css_sp.c b/drivers/staging/media/atomisp/pci/sh_css_sp.c index 615500a7d3c4..0dd58a7fe2cc 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_sp.c +++ b/drivers/staging/media/atomisp/pci/sh_css_sp.c @@ -277,10 +277,10 @@ sh_css_sp_start_raw_copy(struct ia_css_frame *out_frame, ia_css_pipeline_get_sp_thread_id(pipe_num, &thread_id); pipe = &sh_css_sp_group.pipe[thread_id]; - pipe->copy.raw.height = out_frame->info.res.height; - pipe->copy.raw.width = out_frame->info.res.width; - pipe->copy.raw.padded_width = out_frame->info.padded_width; - pipe->copy.raw.raw_bit_depth = out_frame->info.raw_bit_depth; + pipe->copy.raw.height = out_frame->frame_info.res.height; + pipe->copy.raw.width = out_frame->frame_info.res.width; + pipe->copy.raw.padded_width = out_frame->frame_info.padded_width; + pipe->copy.raw.raw_bit_depth = out_frame->frame_info.raw_bit_depth; pipe->copy.raw.max_input_width = max_input_width; pipe->num_stages = 1; pipe->pipe_id = pipe_id; @@ -351,10 +351,10 @@ sh_css_sp_start_isys_copy(struct ia_css_frame *out_frame, ia_css_pipeline_get_sp_thread_id(pipe_num, &thread_id); pipe = &sh_css_sp_group.pipe[thread_id]; - pipe->copy.raw.height = out_frame->info.res.height; - pipe->copy.raw.width = out_frame->info.res.width; - pipe->copy.raw.padded_width = out_frame->info.padded_width; - pipe->copy.raw.raw_bit_depth = out_frame->info.raw_bit_depth; + pipe->copy.raw.height = out_frame->frame_info.res.height; + pipe->copy.raw.width = out_frame->frame_info.res.width; + pipe->copy.raw.padded_width = out_frame->frame_info.padded_width; + pipe->copy.raw.raw_bit_depth = out_frame->frame_info.raw_bit_depth; pipe->copy.raw.max_input_width = max_input_width; pipe->num_stages = 1; pipe->pipe_id = pipe_id; @@ -451,9 +451,9 @@ sh_css_copy_frame_to_spframe(struct ia_css_frame_sp *sp_frame_out, frame_in->data, frame_in->buf_type); - ia_css_frame_info_to_frame_sp_info(&sp_frame_out->info, &frame_in->info); + ia_css_frame_info_to_frame_sp_info(&sp_frame_out->info, &frame_in->frame_info); - switch (frame_in->info.format) { + switch (frame_in->frame_info.format) { case IA_CSS_FRAME_FORMAT_RAW_PACKED: case IA_CSS_FRAME_FORMAT_RAW: sp_frame_out->planes.raw.offset = frame_in->planes.raw.offset; @@ -536,7 +536,7 @@ set_input_frame_buffer(const struct ia_css_frame *frame) if (!frame) return -EINVAL; - switch (frame->info.format) { + switch (frame->frame_info.format) { case IA_CSS_FRAME_FORMAT_QPLANE6: case IA_CSS_FRAME_FORMAT_YUV420_16: case IA_CSS_FRAME_FORMAT_RAW_PACKED: @@ -567,7 +567,7 @@ set_output_frame_buffer(const struct ia_css_frame *frame, if (!frame) return -EINVAL; - switch (frame->info.format) { + switch (frame->frame_info.format) { case IA_CSS_FRAME_FORMAT_YUV420: case IA_CSS_FRAME_FORMAT_YUV422: case IA_CSS_FRAME_FORMAT_YUV444: @@ -608,7 +608,7 @@ set_view_finder_buffer(const struct ia_css_frame *frame) if (!frame) return -EINVAL; - switch (frame->info.format) { + switch (frame->frame_info.format) { /* the dual output pin */ case IA_CSS_FRAME_FORMAT_NV12: case IA_CSS_FRAME_FORMAT_NV12_16: @@ -819,34 +819,35 @@ static int configure_isp_from_args(const struct sh_css_sp_pipeline *pipeline, ret = ia_css_fpn_configure(binary, &binary->in_frame_info); if (ret) return ret; - ret = ia_css_crop_configure(binary, &args->delay_frames[0]->info); + ret = ia_css_crop_configure(binary, ia_css_frame_get_info(args->delay_frames[0])); if (ret) return ret; ret = ia_css_qplane_configure(pipeline, binary, &binary->in_frame_info); if (ret) return ret; - ret = ia_css_output0_configure(binary, &args->out_frame[0]->info); + ret = ia_css_output0_configure(binary, ia_css_frame_get_info(args->out_frame[0])); if (ret) return ret; - ret = ia_css_output1_configure(binary, &args->out_vf_frame->info); + ret = ia_css_output1_configure(binary, ia_css_frame_get_info(args->out_vf_frame)); if (ret) return ret; ret = ia_css_copy_output_configure(binary, args->copy_output); if (ret) return ret; - ret = ia_css_output0_configure(binary, &args->out_frame[0]->info); + ret = ia_css_output0_configure(binary, ia_css_frame_get_info(args->out_frame[0])); if (ret) return ret; - ret = ia_css_iterator_configure(binary, &args->in_frame->info); + ret = ia_css_iterator_configure(binary, ia_css_frame_get_info(args->in_frame)); if (ret) return ret; - ret = ia_css_dvs_configure(binary, &args->out_frame[0]->info); + ret = ia_css_dvs_configure(binary, ia_css_frame_get_info(args->out_frame[0])); if (ret) return ret; - ret = ia_css_output_configure(binary, &args->out_frame[0]->info); + ret = ia_css_output_configure(binary, ia_css_frame_get_info(args->out_frame[0])); if (ret) return ret; - ret = ia_css_raw_configure(pipeline, binary, &args->in_frame->info, &binary->in_frame_info, two_ppc, deinterleaved); + ret = ia_css_raw_configure(pipeline, binary, ia_css_frame_get_info(args->in_frame), + &binary->in_frame_info, two_ppc, deinterleaved); if (ret) return ret; @@ -1037,7 +1038,7 @@ sh_css_sp_init_stage(struct ia_css_binary *binary, return -EINVAL; if (args->in_frame) - ia_css_get_crop_offsets(pipe, &args->in_frame->info); + ia_css_get_crop_offsets(pipe, &args->in_frame->frame_info); else ia_css_get_crop_offsets(pipe, &binary->in_frame_info); #else @@ -1124,15 +1125,14 @@ sp_init_stage(struct ia_css_pipeline_stage *stage, const struct ia_css_frame_info *out_infos[IA_CSS_BINARY_MAX_OUTPUT_PORTS] = {NULL}; if (args->out_frame[0]) - out_infos[0] = &args->out_frame[0]->info; + out_infos[0] = &args->out_frame[0]->frame_info; info = &firmware->info.isp; ia_css_binary_fill_info(info, false, false, ATOMISP_INPUT_FORMAT_RAW_10, - args->in_frame ? &args->in_frame->info : NULL, + ia_css_frame_get_info(args->in_frame), NULL, out_infos, - args->out_vf_frame ? &args->out_vf_frame->info - : NULL, + ia_css_frame_get_info(args->out_vf_frame), &tmp_binary, NULL, -1, true); @@ -1266,7 +1266,7 @@ sh_css_sp_init_pipeline(struct ia_css_pipeline *me, sh_css_sp_group.pipe[thread_id].thread_id = thread_id; sh_css_sp_group.pipe[thread_id].pipe_num = pipe_num; sh_css_sp_group.pipe[thread_id].num_execs = me->num_execs; - sh_css_sp_group.pipe[thread_id].pipe_qos_config = me->pipe_qos_config; + sh_css_sp_group.pipe[thread_id].pipe_qos_config = QOS_INVALID; sh_css_sp_group.pipe[thread_id].required_bds_factor = required_bds_factor; sh_css_sp_group.pipe[thread_id].input_system_mode = (uint32_t)input_mode; diff --git a/drivers/staging/media/deprecated/atmel/Kconfig b/drivers/staging/media/deprecated/atmel/Kconfig new file mode 100644 index 000000000000..418841ea5a0d --- /dev/null +++ b/drivers/staging/media/deprecated/atmel/Kconfig @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "Atmel media platform drivers" + +config VIDEO_ATMEL_ISC + tristate "ATMEL Image Sensor Controller (ISC) support (DEPRECATED)" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && COMMON_CLK + depends on ARCH_AT91 || COMPILE_TEST + depends on !VIDEO_MICROCHIP_ISC_BASE || COMPILE_TEST + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_DMA_CONTIG + select REGMAP_MMIO + select V4L2_FWNODE + select VIDEO_ATMEL_ISC_BASE + help + This module makes the ATMEL Image Sensor Controller available + as a v4l2 device. + + This driver is deprecated and is scheduled for removal by + the beginning of 2026. See the TODO file for more information. + +config VIDEO_ATMEL_XISC + tristate "ATMEL eXtended Image Sensor Controller (XISC) support (DEPRECATED)" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && COMMON_CLK + depends on ARCH_AT91 || COMPILE_TEST + depends on !VIDEO_MICROCHIP_ISC_BASE || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select REGMAP_MMIO + select V4L2_FWNODE + select VIDEO_ATMEL_ISC_BASE + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + help + This module makes the ATMEL eXtended Image Sensor Controller + available as a v4l2 device. + + This driver is deprecated and is scheduled for removal by + the beginning of 2026. See the TODO file for more information. + +config VIDEO_ATMEL_ISC_BASE + tristate + default n + help + ATMEL ISC and XISC common code base. diff --git a/drivers/staging/media/deprecated/atmel/Makefile b/drivers/staging/media/deprecated/atmel/Makefile new file mode 100644 index 000000000000..34eaeeac5bba --- /dev/null +++ b/drivers/staging/media/deprecated/atmel/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only +atmel-isc-objs = atmel-sama5d2-isc.o +atmel-xisc-objs = atmel-sama7g5-isc.o +atmel-isc-common-objs = atmel-isc-base.o atmel-isc-clk.o + +obj-$(CONFIG_VIDEO_ATMEL_ISC_BASE) += atmel-isc-common.o +obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel-isc.o +obj-$(CONFIG_VIDEO_ATMEL_XISC) += atmel-xisc.o diff --git a/drivers/staging/media/deprecated/atmel/TODO b/drivers/staging/media/deprecated/atmel/TODO new file mode 100644 index 000000000000..71691df07a80 --- /dev/null +++ b/drivers/staging/media/deprecated/atmel/TODO @@ -0,0 +1,34 @@ +The Atmel ISC driver is not compliant with media controller specification. +In order to evolve this driver, it has to move to media controller, to +support enhanced features and future products which embed it. +The move to media controller involves several changes which are +not backwards compatible with the current usability of the driver. + +The best example is the way the format is propagated from the top video +driver /dev/videoX down to the sensor. + +In a simple configuration sensor ==> isc , the isc just calls subdev s_fmt +and controls the sensor directly. This is achieved by having a lot of code +inside the driver that will query the subdev at probe time and make a list +of formats which are usable. +Basically the user has nothing to configure, as the isc will handle +everything at the top level. This is an easy way to capture, but also comes +with the drawback of lack of flexibility. +In a more complicated pipeline +sensor ==> controller 1 ==> controller 2 ==> isc +this will not be achievable, as controller 1 and controller 2 might be +media-controller configurable, and will not propagate the formats down to +the sensor. + +After discussions with the media maintainers, the decision is to move +Atmel ISC to staging as-is, to keep the Kconfig symbols and the users +to the driver in staging. Thus, all the existing users of the non +media-controller paradigm will continue to be happy and use the old config +way. + +The new driver was added in the media subsystem with a different +symbol, with the conversion to media controller done, and new users +of the driver will be able to use all the new features. + +The replacement driver is named VIDEO_MICROCHIP_ISC or +VIDEO_MICROCHIP_XISC depending on the product flavor. diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc-base.c b/drivers/staging/media/deprecated/atmel/atmel-isc-base.c new file mode 100644 index 000000000000..99e61bbfc9bc --- /dev/null +++ b/drivers/staging/media/deprecated/atmel/atmel-isc-base.c @@ -0,0 +1,2011 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Microchip Image Sensor Controller (ISC) common driver base + * + * Copyright (C) 2016-2019 Microchip Technology, Inc. + * + * Author: Songjun Wu + * Author: Eugen Hristev <eugen.hristev@microchip.com> + * + */ +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/math64.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/videodev2.h> +#include <linux/atmel-isc-media.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-image-sizes.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> +#include <media/videobuf2-dma-contig.h> + +#include "atmel-isc-regs.h" +#include "atmel-isc.h" + +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "debug level (0-2)"); + +static unsigned int sensor_preferred = 1; +module_param(sensor_preferred, uint, 0644); +MODULE_PARM_DESC(sensor_preferred, + "Sensor is preferred to output the specified format (1-on 0-off), default 1"); + +#define ISC_IS_FORMAT_RAW(mbus_code) \ + (((mbus_code) & 0xf000) == 0x3000) + +#define ISC_IS_FORMAT_GREY(mbus_code) \ + (((mbus_code) == MEDIA_BUS_FMT_Y10_1X10) | \ + (((mbus_code) == MEDIA_BUS_FMT_Y8_1X8))) + +static inline void isc_update_v4l2_ctrls(struct isc_device *isc) +{ + struct isc_ctrls *ctrls = &isc->ctrls; + + /* In here we set the v4l2 controls w.r.t. our pipeline config */ + v4l2_ctrl_s_ctrl(isc->r_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_R]); + v4l2_ctrl_s_ctrl(isc->b_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_B]); + 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, 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) +{ + struct isc_ctrls *ctrls = &isc->ctrls; + + /* In here we set our actual hw pipeline config */ + + regmap_write(isc->regmap, ISC_WB_O_RGR, + ((ctrls->offset[ISC_HIS_CFG_MODE_R])) | + ((ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16)); + regmap_write(isc->regmap, ISC_WB_O_BGB, + ((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)); + regmap_write(isc->regmap, ISC_WB_G_BGB, + ctrls->gain[ISC_HIS_CFG_MODE_B] | + (ctrls->gain[ISC_HIS_CFG_MODE_GB] << 16)); +} + +static inline void isc_reset_awb_ctrls(struct isc_device *isc) +{ + unsigned int c; + + 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 */ + isc->ctrls.offset[c] = 0; + } +} + + +static int isc_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct isc_device *isc = vb2_get_drv_priv(vq); + unsigned int size = isc->fmt.fmt.pix.sizeimage; + + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = size; + + return 0; +} + +static int isc_buffer_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct isc_device *isc = vb2_get_drv_priv(vb->vb2_queue); + unsigned long size = isc->fmt.fmt.pix.sizeimage; + + if (vb2_plane_size(vb, 0) < size) { + v4l2_err(&isc->v4l2_dev, "buffer too small (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, size); + + vbuf->field = isc->fmt.fmt.pix.field; + + return 0; +} + +static void isc_crop_pfe(struct isc_device *isc) +{ + struct regmap *regmap = isc->regmap; + u32 h, w; + + h = isc->fmt.fmt.pix.height; + w = isc->fmt.fmt.pix.width; + + /* + * In case the sensor is not RAW, it will output a pixel (12-16 bits) + * with two samples on the ISC Data bus (which is 8-12) + * ISC will count each sample, so, we need to multiply these values + * by two, to get the real number of samples for the required pixels. + */ + if (!ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) { + h <<= 1; + w <<= 1; + } + + /* + * We limit the column/row count that the ISC will output according + * to the configured resolution that we want. + * This will avoid the situation where the sensor is misconfigured, + * sending more data, and the ISC will just take it and DMA to memory, + * causing corruption. + */ + regmap_write(regmap, ISC_PFE_CFG1, + (ISC_PFE_CFG1_COLMIN(0) & ISC_PFE_CFG1_COLMIN_MASK) | + (ISC_PFE_CFG1_COLMAX(w - 1) & ISC_PFE_CFG1_COLMAX_MASK)); + + regmap_write(regmap, ISC_PFE_CFG2, + (ISC_PFE_CFG2_ROWMIN(0) & ISC_PFE_CFG2_ROWMIN_MASK) | + (ISC_PFE_CFG2_ROWMAX(h - 1) & ISC_PFE_CFG2_ROWMAX_MASK)); + + regmap_update_bits(regmap, ISC_PFE_CFG0, + ISC_PFE_CFG0_COLEN | ISC_PFE_CFG0_ROWEN, + ISC_PFE_CFG0_COLEN | ISC_PFE_CFG0_ROWEN); +} + +static void isc_start_dma(struct isc_device *isc) +{ + struct regmap *regmap = isc->regmap; + u32 sizeimage = isc->fmt.fmt.pix.sizeimage; + u32 dctrl_dview; + dma_addr_t addr0; + + addr0 = vb2_dma_contig_plane_dma_addr(&isc->cur_frm->vb.vb2_buf, 0); + regmap_write(regmap, ISC_DAD0 + isc->offsets.dma, addr0); + + switch (isc->config.fourcc) { + case V4L2_PIX_FMT_YUV420: + regmap_write(regmap, ISC_DAD1 + isc->offsets.dma, + addr0 + (sizeimage * 2) / 3); + regmap_write(regmap, ISC_DAD2 + isc->offsets.dma, + addr0 + (sizeimage * 5) / 6); + break; + case V4L2_PIX_FMT_YUV422P: + regmap_write(regmap, ISC_DAD1 + isc->offsets.dma, + addr0 + sizeimage / 2); + regmap_write(regmap, ISC_DAD2 + isc->offsets.dma, + addr0 + (sizeimage * 3) / 4); + break; + default: + break; + } + + dctrl_dview = isc->config.dctrl_dview; + + regmap_write(regmap, ISC_DCTRL + isc->offsets.dma, + dctrl_dview | ISC_DCTRL_IE_IS); + spin_lock(&isc->awb_lock); + regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_CAPTURE); + spin_unlock(&isc->awb_lock); +} + +static void isc_set_pipeline(struct isc_device *isc, u32 pipeline) +{ + struct regmap *regmap = isc->regmap; + struct isc_ctrls *ctrls = &isc->ctrls; + u32 val, bay_cfg; + const u32 *gamma; + unsigned int i; + + /* WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB422-->SUB420 */ + for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) { + val = pipeline & BIT(i) ? 1 : 0; + regmap_field_write(isc->pipeline[i], val); + } + + if (!pipeline) + return; + + bay_cfg = isc->config.sd_format->cfa_baycfg; + + regmap_write(regmap, ISC_WB_CFG, bay_cfg); + isc_update_awb_ctrls(isc); + isc_update_v4l2_ctrls(isc); + + regmap_write(regmap, ISC_CFA_CFG, bay_cfg | ISC_CFA_CFG_EITPOL); + + gamma = &isc->gamma_table[ctrls->gamma_index][0]; + regmap_bulk_write(regmap, ISC_GAM_BENTRY, gamma, GAMMA_ENTRIES); + regmap_bulk_write(regmap, ISC_GAM_GENTRY, gamma, GAMMA_ENTRIES); + regmap_bulk_write(regmap, ISC_GAM_RENTRY, gamma, GAMMA_ENTRIES); + + isc->config_dpc(isc); + isc->config_csc(isc); + isc->config_cbc(isc); + isc->config_cc(isc); + isc->config_gam(isc); +} + +static int isc_update_profile(struct isc_device *isc) +{ + struct regmap *regmap = isc->regmap; + u32 sr; + int counter = 100; + + regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_UPPRO); + + regmap_read(regmap, ISC_CTRLSR, &sr); + while ((sr & ISC_CTRL_UPPRO) && counter--) { + usleep_range(1000, 2000); + regmap_read(regmap, ISC_CTRLSR, &sr); + } + + if (counter < 0) { + v4l2_warn(&isc->v4l2_dev, "Time out to update profile\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static void isc_set_histogram(struct isc_device *isc, bool enable) +{ + struct regmap *regmap = isc->regmap; + struct isc_ctrls *ctrls = &isc->ctrls; + + if (enable) { + regmap_write(regmap, ISC_HIS_CFG + isc->offsets.his, + ISC_HIS_CFG_MODE_GR | + (isc->config.sd_format->cfa_baycfg + << ISC_HIS_CFG_BAYSEL_SHIFT) | + ISC_HIS_CFG_RAR); + regmap_write(regmap, ISC_HIS_CTRL + isc->offsets.his, + ISC_HIS_CTRL_EN); + regmap_write(regmap, ISC_INTEN, ISC_INT_HISDONE); + ctrls->hist_id = ISC_HIS_CFG_MODE_GR; + isc_update_profile(isc); + regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ); + + ctrls->hist_stat = HIST_ENABLED; + } else { + regmap_write(regmap, ISC_INTDIS, ISC_INT_HISDONE); + regmap_write(regmap, ISC_HIS_CTRL + isc->offsets.his, + ISC_HIS_CTRL_DIS); + + ctrls->hist_stat = HIST_DISABLED; + } +} + +static int isc_configure(struct isc_device *isc) +{ + struct regmap *regmap = isc->regmap; + u32 pfe_cfg0, dcfg, mask, pipeline; + struct isc_subdev_entity *subdev = isc->current_subdev; + + pfe_cfg0 = isc->config.sd_format->pfe_cfg0_bps; + pipeline = isc->config.bits_pipeline; + + dcfg = isc->config.dcfg_imode | isc->dcfg; + + pfe_cfg0 |= subdev->pfe_cfg0 | ISC_PFE_CFG0_MODE_PROGRESSIVE; + mask = ISC_PFE_CFG0_BPS_MASK | ISC_PFE_CFG0_HPOL_LOW | + ISC_PFE_CFG0_VPOL_LOW | ISC_PFE_CFG0_PPOL_LOW | + ISC_PFE_CFG0_MODE_MASK | ISC_PFE_CFG0_CCIR_CRC | + ISC_PFE_CFG0_CCIR656 | ISC_PFE_CFG0_MIPI; + + regmap_update_bits(regmap, ISC_PFE_CFG0, mask, pfe_cfg0); + + isc->config_rlp(isc); + + regmap_write(regmap, ISC_DCFG + isc->offsets.dma, dcfg); + + /* Set the pipeline */ + isc_set_pipeline(isc, pipeline); + + /* + * The current implemented histogram is available for RAW R, B, GB, GR + * channels. We need to check if sensor is outputting RAW BAYER + */ + if (isc->ctrls.awb && + ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) + isc_set_histogram(isc, true); + else + isc_set_histogram(isc, false); + + /* Update profile */ + return isc_update_profile(isc); +} + +static int isc_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct isc_device *isc = vb2_get_drv_priv(vq); + struct regmap *regmap = isc->regmap; + struct isc_buffer *buf; + unsigned long flags; + int ret; + + /* Enable stream on the sub device */ + ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 1); + if (ret && ret != -ENOIOCTLCMD) { + v4l2_err(&isc->v4l2_dev, "stream on failed in subdev %d\n", + ret); + goto err_start_stream; + } + + ret = pm_runtime_resume_and_get(isc->dev); + if (ret < 0) { + v4l2_err(&isc->v4l2_dev, "RPM resume failed in subdev %d\n", + ret); + goto err_pm_get; + } + + ret = isc_configure(isc); + if (unlikely(ret)) + goto err_configure; + + /* Enable DMA interrupt */ + regmap_write(regmap, ISC_INTEN, ISC_INT_DDONE); + + spin_lock_irqsave(&isc->dma_queue_lock, flags); + + isc->sequence = 0; + isc->stop = false; + reinit_completion(&isc->comp); + + isc->cur_frm = list_first_entry(&isc->dma_queue, + struct isc_buffer, list); + list_del(&isc->cur_frm->list); + + isc_crop_pfe(isc); + isc_start_dma(isc); + + spin_unlock_irqrestore(&isc->dma_queue_lock, flags); + + /* if we streaming from RAW, we can do one-shot white balance adj */ + if (ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) + v4l2_ctrl_activate(isc->do_wb_ctrl, true); + + return 0; + +err_configure: + pm_runtime_put_sync(isc->dev); +err_pm_get: + v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0); + +err_start_stream: + spin_lock_irqsave(&isc->dma_queue_lock, flags); + list_for_each_entry(buf, &isc->dma_queue, list) + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + INIT_LIST_HEAD(&isc->dma_queue); + spin_unlock_irqrestore(&isc->dma_queue_lock, flags); + + return ret; +} + +static void isc_stop_streaming(struct vb2_queue *vq) +{ + struct isc_device *isc = vb2_get_drv_priv(vq); + unsigned long flags; + struct isc_buffer *buf; + int ret; + + mutex_lock(&isc->awb_mutex); + v4l2_ctrl_activate(isc->do_wb_ctrl, false); + + isc->stop = true; + + /* Wait until the end of the current frame */ + if (isc->cur_frm && !wait_for_completion_timeout(&isc->comp, 5 * HZ)) + v4l2_err(&isc->v4l2_dev, + "Timeout waiting for end of the capture\n"); + + mutex_unlock(&isc->awb_mutex); + + /* Disable DMA interrupt */ + regmap_write(isc->regmap, ISC_INTDIS, ISC_INT_DDONE); + + pm_runtime_put_sync(isc->dev); + + /* Disable stream on the sub device */ + ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0); + if (ret && ret != -ENOIOCTLCMD) + v4l2_err(&isc->v4l2_dev, "stream off failed in subdev\n"); + + /* Release all active buffers */ + spin_lock_irqsave(&isc->dma_queue_lock, flags); + if (unlikely(isc->cur_frm)) { + vb2_buffer_done(&isc->cur_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); + isc->cur_frm = NULL; + } + list_for_each_entry(buf, &isc->dma_queue, list) + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + INIT_LIST_HEAD(&isc->dma_queue); + spin_unlock_irqrestore(&isc->dma_queue_lock, flags); +} + +static void isc_buffer_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct isc_buffer *buf = container_of(vbuf, struct isc_buffer, vb); + struct isc_device *isc = vb2_get_drv_priv(vb->vb2_queue); + unsigned long flags; + + spin_lock_irqsave(&isc->dma_queue_lock, flags); + if (!isc->cur_frm && list_empty(&isc->dma_queue) && + vb2_start_streaming_called(vb->vb2_queue)) { + isc->cur_frm = buf; + isc_start_dma(isc); + } else + list_add_tail(&buf->list, &isc->dma_queue); + spin_unlock_irqrestore(&isc->dma_queue_lock, flags); +} + +static struct isc_format *find_format_by_fourcc(struct isc_device *isc, + unsigned int fourcc) +{ + unsigned int num_formats = isc->num_user_formats; + struct isc_format *fmt; + unsigned int i; + + for (i = 0; i < num_formats; i++) { + fmt = isc->user_formats[i]; + if (fmt->fourcc == fourcc) + return fmt; + } + + return NULL; +} + +static const struct vb2_ops isc_vb2_ops = { + .queue_setup = isc_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_prepare = isc_buffer_prepare, + .start_streaming = isc_start_streaming, + .stop_streaming = isc_stop_streaming, + .buf_queue = isc_buffer_queue, +}; + +static int isc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct isc_device *isc = video_drvdata(file); + + strscpy(cap->driver, "microchip-isc", sizeof(cap->driver)); + strscpy(cap->card, "Atmel Image Sensor Controller", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", isc->v4l2_dev.name); + + return 0; +} + +static int isc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct isc_device *isc = video_drvdata(file); + u32 index = f->index; + u32 i, supported_index; + + if (index < isc->controller_formats_size) { + f->pixelformat = isc->controller_formats[index].fourcc; + return 0; + } + + index -= isc->controller_formats_size; + + supported_index = 0; + + for (i = 0; i < isc->formats_list_size; i++) { + if (!ISC_IS_FORMAT_RAW(isc->formats_list[i].mbus_code) || + !isc->formats_list[i].sd_support) + continue; + if (supported_index == index) { + f->pixelformat = isc->formats_list[i].fourcc; + return 0; + } + supported_index++; + } + + return -EINVAL; +} + +static int isc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct isc_device *isc = video_drvdata(file); + + *fmt = isc->fmt; + + return 0; +} + +/* + * Checks the current configured format, if ISC can output it, + * considering which type of format the ISC receives from the sensor + */ +static int isc_try_validate_formats(struct isc_device *isc) +{ + int ret; + bool bayer = false, yuv = false, rgb = false, grey = false; + + /* all formats supported by the RLP module are OK */ + switch (isc->try_config.fourcc) { + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: + case V4L2_PIX_FMT_SBGGR10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SRGGB10: + case V4L2_PIX_FMT_SBGGR12: + case V4L2_PIX_FMT_SGBRG12: + case V4L2_PIX_FMT_SGRBG12: + case V4L2_PIX_FMT_SRGGB12: + ret = 0; + bayer = true; + break; + + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YUV422P: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + ret = 0; + yuv = true; + break; + + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_ABGR32: + case V4L2_PIX_FMT_XBGR32: + case V4L2_PIX_FMT_ARGB444: + case V4L2_PIX_FMT_ARGB555: + ret = 0; + rgb = true; + break; + case V4L2_PIX_FMT_GREY: + case V4L2_PIX_FMT_Y10: + case V4L2_PIX_FMT_Y16: + ret = 0; + grey = true; + break; + default: + /* any other different formats are not supported */ + ret = -EINVAL; + } + v4l2_dbg(1, debug, &isc->v4l2_dev, + "Format validation, requested rgb=%u, yuv=%u, grey=%u, bayer=%u\n", + rgb, yuv, grey, bayer); + + /* we cannot output RAW if we do not receive RAW */ + if ((bayer) && !ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) + return -EINVAL; + + /* we cannot output GREY if we do not receive RAW/GREY */ + if (grey && !ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code) && + !ISC_IS_FORMAT_GREY(isc->try_config.sd_format->mbus_code)) + return -EINVAL; + + return ret; +} + +/* + * Configures the RLP and DMA modules, depending on the output format + * configured for the ISC. + * If direct_dump == true, just dump raw data 8/16 bits depending on format. + */ +static int isc_try_configure_rlp_dma(struct isc_device *isc, bool direct_dump) +{ + isc->try_config.rlp_cfg_mode = 0; + + switch (isc->try_config.fourcc) { + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: + isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT8; + isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8; + isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; + isc->try_config.bpp = 8; + isc->try_config.bpp_v4l2 = 8; + break; + case V4L2_PIX_FMT_SBGGR10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SRGGB10: + isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT10; + isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; + isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; + isc->try_config.bpp = 16; + isc->try_config.bpp_v4l2 = 16; + break; + case V4L2_PIX_FMT_SBGGR12: + case V4L2_PIX_FMT_SGBRG12: + case V4L2_PIX_FMT_SGRBG12: + case V4L2_PIX_FMT_SRGGB12: + isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT12; + isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; + isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; + isc->try_config.bpp = 16; + isc->try_config.bpp_v4l2 = 16; + break; + case V4L2_PIX_FMT_RGB565: + isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_RGB565; + isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; + isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; + isc->try_config.bpp = 16; + isc->try_config.bpp_v4l2 = 16; + break; + case V4L2_PIX_FMT_ARGB444: + isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB444; + isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; + isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; + isc->try_config.bpp = 16; + isc->try_config.bpp_v4l2 = 16; + break; + case V4L2_PIX_FMT_ARGB555: + isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB555; + isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; + isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; + isc->try_config.bpp = 16; + isc->try_config.bpp_v4l2 = 16; + break; + case V4L2_PIX_FMT_ABGR32: + case V4L2_PIX_FMT_XBGR32: + isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB32; + isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; + isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; + isc->try_config.bpp = 32; + isc->try_config.bpp_v4l2 = 32; + break; + case V4L2_PIX_FMT_YUV420: + isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YYCC; + isc->try_config.dcfg_imode = ISC_DCFG_IMODE_YC420P; + isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PLANAR; + isc->try_config.bpp = 12; + isc->try_config.bpp_v4l2 = 8; /* only first plane */ + break; + case V4L2_PIX_FMT_YUV422P: + isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YYCC; + isc->try_config.dcfg_imode = ISC_DCFG_IMODE_YC422P; + isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PLANAR; + isc->try_config.bpp = 16; + isc->try_config.bpp_v4l2 = 8; /* only first plane */ + break; + case V4L2_PIX_FMT_YUYV: + isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_YUYV; + isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; + isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; + isc->try_config.bpp = 16; + isc->try_config.bpp_v4l2 = 16; + break; + case V4L2_PIX_FMT_UYVY: + isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_UYVY; + isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; + isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; + isc->try_config.bpp = 16; + isc->try_config.bpp_v4l2 = 16; + break; + case V4L2_PIX_FMT_VYUY: + isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_VYUY; + isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; + isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; + isc->try_config.bpp = 16; + isc->try_config.bpp_v4l2 = 16; + break; + case V4L2_PIX_FMT_GREY: + isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DATY8; + isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8; + isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; + isc->try_config.bpp = 8; + isc->try_config.bpp_v4l2 = 8; + break; + case V4L2_PIX_FMT_Y16: + isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DATY10 | ISC_RLP_CFG_LSH; + fallthrough; + case V4L2_PIX_FMT_Y10: + isc->try_config.rlp_cfg_mode |= ISC_RLP_CFG_MODE_DATY10; + isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; + isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; + isc->try_config.bpp = 16; + isc->try_config.bpp_v4l2 = 16; + break; + default: + return -EINVAL; + } + + if (direct_dump) { + isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT8; + isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8; + isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; + return 0; + } + + return 0; +} + +/* + * Configuring pipeline modules, depending on which format the ISC outputs + * and considering which format it has as input from the sensor. + */ +static int isc_try_configure_pipeline(struct isc_device *isc) +{ + switch (isc->try_config.fourcc) { + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_ARGB555: + case V4L2_PIX_FMT_ARGB444: + case V4L2_PIX_FMT_ABGR32: + case V4L2_PIX_FMT_XBGR32: + /* if sensor format is RAW, we convert inside ISC */ + if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { + isc->try_config.bits_pipeline = CFA_ENABLE | + WB_ENABLE | GAM_ENABLES | DPC_BLCENABLE | + CC_ENABLE; + } else { + isc->try_config.bits_pipeline = 0x0; + } + break; + case V4L2_PIX_FMT_YUV420: + /* if sensor format is RAW, we convert inside ISC */ + if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { + isc->try_config.bits_pipeline = CFA_ENABLE | + CSC_ENABLE | GAM_ENABLES | WB_ENABLE | + SUB420_ENABLE | SUB422_ENABLE | CBC_ENABLE | + DPC_BLCENABLE; + } else { + isc->try_config.bits_pipeline = 0x0; + } + break; + case V4L2_PIX_FMT_YUV422P: + /* if sensor format is RAW, we convert inside ISC */ + if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { + isc->try_config.bits_pipeline = CFA_ENABLE | + CSC_ENABLE | WB_ENABLE | GAM_ENABLES | + SUB422_ENABLE | CBC_ENABLE | DPC_BLCENABLE; + } else { + isc->try_config.bits_pipeline = 0x0; + } + break; + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + /* if sensor format is RAW, we convert inside ISC */ + if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { + isc->try_config.bits_pipeline = CFA_ENABLE | + CSC_ENABLE | WB_ENABLE | GAM_ENABLES | + SUB422_ENABLE | CBC_ENABLE | DPC_BLCENABLE; + } else { + isc->try_config.bits_pipeline = 0x0; + } + break; + case V4L2_PIX_FMT_GREY: + case V4L2_PIX_FMT_Y16: + /* if sensor format is RAW, we convert inside ISC */ + if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { + isc->try_config.bits_pipeline = CFA_ENABLE | + CSC_ENABLE | WB_ENABLE | GAM_ENABLES | + CBC_ENABLE | DPC_BLCENABLE; + } else { + isc->try_config.bits_pipeline = 0x0; + } + break; + default: + if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) + isc->try_config.bits_pipeline = WB_ENABLE | DPC_BLCENABLE; + else + isc->try_config.bits_pipeline = 0x0; + } + + /* Tune the pipeline to product specific */ + isc->adapt_pipeline(isc); + + return 0; +} + +static void isc_try_fse(struct isc_device *isc, + struct v4l2_subdev_state *sd_state) +{ + int ret; + struct v4l2_subdev_frame_size_enum fse = {}; + + /* + * If we do not know yet which format the subdev is using, we cannot + * do anything. + */ + if (!isc->try_config.sd_format) + return; + + fse.code = isc->try_config.sd_format->mbus_code; + fse.which = V4L2_SUBDEV_FORMAT_TRY; + + ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size, + sd_state, &fse); + /* + * Attempt to obtain format size from subdev. If not available, + * just use the maximum ISC can receive. + */ + if (ret) { + sd_state->pads->try_crop.width = isc->max_width; + sd_state->pads->try_crop.height = isc->max_height; + } else { + sd_state->pads->try_crop.width = fse.max_width; + sd_state->pads->try_crop.height = fse.max_height; + } +} + +static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f, + u32 *code) +{ + int i; + struct isc_format *sd_fmt = NULL, *direct_fmt = NULL; + struct v4l2_pix_format *pixfmt = &f->fmt.pix; + struct v4l2_subdev_pad_config pad_cfg = {}; + struct v4l2_subdev_state pad_state = { + .pads = &pad_cfg + }; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_TRY, + }; + u32 mbus_code; + int ret; + bool rlp_dma_direct_dump = false; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* Step 1: find a RAW format that is supported */ + for (i = 0; i < isc->num_user_formats; i++) { + if (ISC_IS_FORMAT_RAW(isc->user_formats[i]->mbus_code)) { + sd_fmt = isc->user_formats[i]; + break; + } + } + /* Step 2: We can continue with this RAW format, or we can look + * for better: maybe sensor supports directly what we need. + */ + direct_fmt = find_format_by_fourcc(isc, pixfmt->pixelformat); + + /* Step 3: We have both. We decide given the module parameter which + * one to use. + */ + if (direct_fmt && sd_fmt && sensor_preferred) + sd_fmt = direct_fmt; + + /* Step 4: we do not have RAW but we have a direct format. Use it. */ + if (direct_fmt && !sd_fmt) + sd_fmt = direct_fmt; + + /* Step 5: if we are using a direct format, we need to package + * everything as 8 bit data and just dump it + */ + if (sd_fmt == direct_fmt) + rlp_dma_direct_dump = true; + + /* Step 6: We have no format. This can happen if the userspace + * requests some weird/invalid format. + * In this case, default to whatever we have + */ + if (!sd_fmt && !direct_fmt) { + sd_fmt = isc->user_formats[isc->num_user_formats - 1]; + v4l2_dbg(1, debug, &isc->v4l2_dev, + "Sensor not supporting %.4s, using %.4s\n", + (char *)&pixfmt->pixelformat, (char *)&sd_fmt->fourcc); + } + + if (!sd_fmt) { + ret = -EINVAL; + goto isc_try_fmt_err; + } + + /* Step 7: Print out what we decided for debugging */ + v4l2_dbg(1, debug, &isc->v4l2_dev, + "Preferring to have sensor using format %.4s\n", + (char *)&sd_fmt->fourcc); + + /* Step 8: at this moment we decided which format the subdev will use */ + isc->try_config.sd_format = sd_fmt; + + /* Limit to Atmel ISC hardware capabilities */ + if (pixfmt->width > isc->max_width) + pixfmt->width = isc->max_width; + if (pixfmt->height > isc->max_height) + pixfmt->height = isc->max_height; + + /* + * The mbus format is the one the subdev outputs. + * The pixels will be transferred in this format Sensor -> ISC + */ + mbus_code = sd_fmt->mbus_code; + + /* + * Validate formats. If the required format is not OK, default to raw. + */ + + isc->try_config.fourcc = pixfmt->pixelformat; + + if (isc_try_validate_formats(isc)) { + pixfmt->pixelformat = isc->try_config.fourcc = sd_fmt->fourcc; + /* Re-try to validate the new format */ + ret = isc_try_validate_formats(isc); + if (ret) + goto isc_try_fmt_err; + } + + ret = isc_try_configure_rlp_dma(isc, rlp_dma_direct_dump); + if (ret) + goto isc_try_fmt_err; + + ret = isc_try_configure_pipeline(isc); + if (ret) + goto isc_try_fmt_err; + + /* Obtain frame sizes if possible to have crop requirements ready */ + isc_try_fse(isc, &pad_state); + + v4l2_fill_mbus_format(&format.format, pixfmt, mbus_code); + ret = v4l2_subdev_call(isc->current_subdev->sd, pad, set_fmt, + &pad_state, &format); + if (ret < 0) + goto isc_try_fmt_subdev_err; + + v4l2_fill_pix_format(pixfmt, &format.format); + + /* Limit to Atmel ISC hardware capabilities */ + if (pixfmt->width > isc->max_width) + pixfmt->width = isc->max_width; + if (pixfmt->height > isc->max_height) + pixfmt->height = isc->max_height; + + pixfmt->field = V4L2_FIELD_NONE; + pixfmt->bytesperline = (pixfmt->width * isc->try_config.bpp_v4l2) >> 3; + pixfmt->sizeimage = ((pixfmt->width * isc->try_config.bpp) >> 3) * + pixfmt->height; + + if (code) + *code = mbus_code; + + return 0; + +isc_try_fmt_err: + v4l2_err(&isc->v4l2_dev, "Could not find any possible format for a working pipeline\n"); +isc_try_fmt_subdev_err: + memset(&isc->try_config, 0, sizeof(isc->try_config)); + + return ret; +} + +static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f) +{ + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + u32 mbus_code = 0; + int ret; + + ret = isc_try_fmt(isc, f, &mbus_code); + if (ret) + return ret; + + v4l2_fill_mbus_format(&format.format, &f->fmt.pix, mbus_code); + ret = v4l2_subdev_call(isc->current_subdev->sd, pad, + set_fmt, NULL, &format); + if (ret < 0) + return ret; + + /* Limit to Atmel ISC hardware capabilities */ + if (f->fmt.pix.width > isc->max_width) + f->fmt.pix.width = isc->max_width; + if (f->fmt.pix.height > isc->max_height) + f->fmt.pix.height = isc->max_height; + + isc->fmt = *f; + + if (isc->try_config.sd_format && isc->config.sd_format && + isc->try_config.sd_format != isc->config.sd_format) { + isc->ctrls.hist_stat = HIST_INIT; + isc_reset_awb_ctrls(isc); + isc_update_v4l2_ctrls(isc); + } + /* make the try configuration active */ + isc->config = isc->try_config; + + v4l2_dbg(1, debug, &isc->v4l2_dev, "New ISC configuration in place\n"); + + return 0; +} + +static int isc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct isc_device *isc = video_drvdata(file); + + if (vb2_is_busy(&isc->vb2_vidq)) + return -EBUSY; + + return isc_set_fmt(isc, f); +} + +static int isc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct isc_device *isc = video_drvdata(file); + + return isc_try_fmt(isc, f, NULL); +} + +static int isc_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + if (inp->index != 0) + return -EINVAL; + + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = 0; + strscpy(inp->name, "Camera", sizeof(inp->name)); + + return 0; +} + +static int isc_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + + return 0; +} + +static int isc_s_input(struct file *file, void *priv, unsigned int i) +{ + if (i > 0) + return -EINVAL; + + return 0; +} + +static int isc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct isc_device *isc = video_drvdata(file); + + return v4l2_g_parm_cap(video_devdata(file), isc->current_subdev->sd, a); +} + +static int isc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct isc_device *isc = video_drvdata(file); + + return v4l2_s_parm_cap(video_devdata(file), isc->current_subdev->sd, a); +} + +static int isc_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct isc_device *isc = video_drvdata(file); + int ret = -EINVAL; + int i; + + if (fsize->index) + return -EINVAL; + + for (i = 0; i < isc->num_user_formats; i++) + if (isc->user_formats[i]->fourcc == fsize->pixel_format) + ret = 0; + + for (i = 0; i < isc->controller_formats_size; i++) + if (isc->controller_formats[i].fourcc == fsize->pixel_format) + ret = 0; + + if (ret) + return ret; + + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + + fsize->stepwise.min_width = 16; + fsize->stepwise.max_width = isc->max_width; + fsize->stepwise.min_height = 16; + fsize->stepwise.max_height = isc->max_height; + fsize->stepwise.step_width = 1; + fsize->stepwise.step_height = 1; + + return 0; +} + +static const struct v4l2_ioctl_ops isc_ioctl_ops = { + .vidioc_querycap = isc_querycap, + .vidioc_enum_fmt_vid_cap = isc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = isc_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = isc_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = isc_try_fmt_vid_cap, + + .vidioc_enum_input = isc_enum_input, + .vidioc_g_input = isc_g_input, + .vidioc_s_input = isc_s_input, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_g_parm = isc_g_parm, + .vidioc_s_parm = isc_s_parm, + .vidioc_enum_framesizes = isc_enum_framesizes, + + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static int isc_open(struct file *file) +{ + struct isc_device *isc = video_drvdata(file); + struct v4l2_subdev *sd = isc->current_subdev->sd; + int ret; + + if (mutex_lock_interruptible(&isc->lock)) + return -ERESTARTSYS; + + ret = v4l2_fh_open(file); + if (ret < 0) + goto unlock; + + if (!v4l2_fh_is_singular_file(file)) + goto unlock; + + ret = v4l2_subdev_call(sd, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) { + v4l2_fh_release(file); + goto unlock; + } + + ret = isc_set_fmt(isc, &isc->fmt); + if (ret) { + v4l2_subdev_call(sd, core, s_power, 0); + v4l2_fh_release(file); + } + +unlock: + mutex_unlock(&isc->lock); + return ret; +} + +static int isc_release(struct file *file) +{ + struct isc_device *isc = video_drvdata(file); + struct v4l2_subdev *sd = isc->current_subdev->sd; + bool fh_singular; + int ret; + + mutex_lock(&isc->lock); + + fh_singular = v4l2_fh_is_singular_file(file); + + ret = _vb2_fop_release(file, NULL); + + if (fh_singular) + v4l2_subdev_call(sd, core, s_power, 0); + + mutex_unlock(&isc->lock); + + return ret; +} + +static const struct v4l2_file_operations isc_fops = { + .owner = THIS_MODULE, + .open = isc_open, + .release = isc_release, + .unlocked_ioctl = video_ioctl2, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, +}; + +irqreturn_t atmel_isc_interrupt(int irq, void *dev_id) +{ + struct isc_device *isc = (struct isc_device *)dev_id; + struct regmap *regmap = isc->regmap; + u32 isc_intsr, isc_intmask, pending; + irqreturn_t ret = IRQ_NONE; + + regmap_read(regmap, ISC_INTSR, &isc_intsr); + regmap_read(regmap, ISC_INTMASK, &isc_intmask); + + pending = isc_intsr & isc_intmask; + + if (likely(pending & ISC_INT_DDONE)) { + spin_lock(&isc->dma_queue_lock); + if (isc->cur_frm) { + struct vb2_v4l2_buffer *vbuf = &isc->cur_frm->vb; + struct vb2_buffer *vb = &vbuf->vb2_buf; + + vb->timestamp = ktime_get_ns(); + vbuf->sequence = isc->sequence++; + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + isc->cur_frm = NULL; + } + + if (!list_empty(&isc->dma_queue) && !isc->stop) { + isc->cur_frm = list_first_entry(&isc->dma_queue, + struct isc_buffer, list); + list_del(&isc->cur_frm->list); + + isc_start_dma(isc); + } + + if (isc->stop) + complete(&isc->comp); + + ret = IRQ_HANDLED; + spin_unlock(&isc->dma_queue_lock); + } + + if (pending & ISC_INT_HISDONE) { + schedule_work(&isc->awb_work); + ret = IRQ_HANDLED; + } + + return ret; +} +EXPORT_SYMBOL_GPL(atmel_isc_interrupt); + +static void isc_hist_count(struct isc_device *isc, u32 *min, u32 *max) +{ + struct regmap *regmap = isc->regmap; + struct isc_ctrls *ctrls = &isc->ctrls; + u32 *hist_count = &ctrls->hist_count[ctrls->hist_id]; + u32 *hist_entry = &ctrls->hist_entry[0]; + u32 i; + + *min = 0; + *max = HIST_ENTRIES; + + regmap_bulk_read(regmap, ISC_HIS_ENTRY + isc->offsets.his_entry, + hist_entry, HIST_ENTRIES); + + *hist_count = 0; + /* + * we deliberately ignore the end of the histogram, + * the most white pixels + */ + for (i = 1; i < HIST_ENTRIES; i++) { + if (*hist_entry && !*min) + *min = i; + if (*hist_entry) + *max = i; + *hist_count += i * (*hist_entry++); + } + + if (!*min) + *min = 1; + + v4l2_dbg(1, debug, &isc->v4l2_dev, + "isc wb: hist_id %u, hist_count %u", + ctrls->hist_id, *hist_count); +} + +static void isc_wb_update(struct isc_ctrls *ctrls) +{ + struct isc_device *isc = container_of(ctrls, struct isc_device, ctrls); + u32 *hist_count = &ctrls->hist_count[0]; + u32 c, offset[4]; + u64 avg = 0; + /* We compute two gains, stretch gain and grey world gain */ + u32 s_gain[4], gw_gain[4]; + + /* + * According to Grey World, we need to set gains for R/B to normalize + * them towards the green channel. + * Thus we want to keep Green as fixed and adjust only Red/Blue + * Compute the average of the both green channels first + */ + avg = (u64)hist_count[ISC_HIS_CFG_MODE_GR] + + (u64)hist_count[ISC_HIS_CFG_MODE_GB]; + avg >>= 1; + + v4l2_dbg(1, debug, &isc->v4l2_dev, + "isc wb: green components average %llu\n", avg); + + /* Green histogram is null, nothing to do */ + if (!avg) + return; + + for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) { + /* + * the color offset is the minimum value of the histogram. + * we stretch this color to the full range by substracting + * this value from the color component. + */ + offset[c] = ctrls->hist_minmax[c][HIST_MIN_INDEX]; + /* + * The offset is always at least 1. If the offset is 1, we do + * not need to adjust it, so our result must be zero. + * the offset is computed in a histogram on 9 bits (0..512) + * but the offset in register is based on + * 12 bits pipeline (0..4096). + * we need to shift with the 3 bits that the histogram is + * ignoring + */ + ctrls->offset[c] = (offset[c] - 1) << 3; + + /* + * 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 + * divided by the actual range of color component (Max - Min) + * If we compute gain like this, the actual color component + * will be stretched to the full histogram. + * We need to shift 9 bits for precision, we have 9 bits for + * decimals + */ + s_gain[c] = (HIST_ENTRIES << 9) / + (ctrls->hist_minmax[c][HIST_MAX_INDEX] - + ctrls->hist_minmax[c][HIST_MIN_INDEX] + 1); + + /* + * Now we have to compute the gain w.r.t. the average. + * Add/lose gain to the component towards the average. + * If it happens that the component is zero, use the + * fixed point value : 1.0 gain. + */ + if (hist_count[c]) + gw_gain[c] = div_u64(avg << 9, hist_count[c]); + else + gw_gain[c] = 1 << 9; + + v4l2_dbg(1, debug, &isc->v4l2_dev, + "isc wb: component %d, s_gain %u, gw_gain %u\n", + c, s_gain[c], gw_gain[c]); + /* multiply both gains and adjust for decimals */ + ctrls->gain[c] = s_gain[c] * gw_gain[c]; + ctrls->gain[c] >>= 9; + + /* make sure we are not out of range */ + ctrls->gain[c] = clamp_val(ctrls->gain[c], 0, GENMASK(12, 0)); + + v4l2_dbg(1, debug, &isc->v4l2_dev, + "isc wb: component %d, final gain %u\n", + c, ctrls->gain[c]); + } +} + +static void isc_awb_work(struct work_struct *w) +{ + struct isc_device *isc = + container_of(w, struct isc_device, awb_work); + struct regmap *regmap = isc->regmap; + struct isc_ctrls *ctrls = &isc->ctrls; + u32 hist_id = ctrls->hist_id; + u32 baysel; + unsigned long flags; + u32 min, max; + int ret; + + if (ctrls->hist_stat != HIST_ENABLED) + return; + + isc_hist_count(isc, &min, &max); + + v4l2_dbg(1, debug, &isc->v4l2_dev, + "isc wb mode %d: hist min %u , max %u\n", hist_id, min, max); + + ctrls->hist_minmax[hist_id][HIST_MIN_INDEX] = min; + ctrls->hist_minmax[hist_id][HIST_MAX_INDEX] = max; + + if (hist_id != ISC_HIS_CFG_MODE_B) { + hist_id++; + } else { + isc_wb_update(ctrls); + hist_id = ISC_HIS_CFG_MODE_GR; + } + + ctrls->hist_id = hist_id; + baysel = isc->config.sd_format->cfa_baycfg << ISC_HIS_CFG_BAYSEL_SHIFT; + + ret = pm_runtime_resume_and_get(isc->dev); + if (ret < 0) + return; + + /* + * only update if we have all the required histograms and controls + * if awb has been disabled, we need to reset registers as well. + */ + if (hist_id == ISC_HIS_CFG_MODE_GR || ctrls->awb == ISC_WB_NONE) { + /* + * It may happen that DMA Done IRQ will trigger while we are + * updating white balance registers here. + * In that case, only parts of the controls have been updated. + * We can avoid that by locking the section. + */ + spin_lock_irqsave(&isc->awb_lock, flags); + isc_update_awb_ctrls(isc); + spin_unlock_irqrestore(&isc->awb_lock, flags); + + /* + * if we are doing just the one time white balance adjustment, + * we are basically done. + */ + if (ctrls->awb == ISC_WB_ONETIME) { + v4l2_info(&isc->v4l2_dev, + "Completed one time white-balance adjustment.\n"); + /* update the v4l2 controls values */ + isc_update_v4l2_ctrls(isc); + ctrls->awb = ISC_WB_NONE; + } + } + regmap_write(regmap, ISC_HIS_CFG + isc->offsets.his, + hist_id | baysel | ISC_HIS_CFG_RAR); + + /* + * We have to make sure the streaming has not stopped meanwhile. + * ISC requires a frame to clock the internal profile update. + * To avoid issues, lock the sequence with a mutex + */ + mutex_lock(&isc->awb_mutex); + + /* streaming is not active anymore */ + if (isc->stop) { + mutex_unlock(&isc->awb_mutex); + return; + } + + isc_update_profile(isc); + + mutex_unlock(&isc->awb_mutex); + + /* if awb has been disabled, we don't need to start another histogram */ + if (ctrls->awb) + regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ); + + pm_runtime_put_sync(isc->dev); +} + +static int isc_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct isc_device *isc = container_of(ctrl->handler, + struct isc_device, ctrls.handler); + struct isc_ctrls *ctrls = &isc->ctrls; + + if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrls->brightness = ctrl->val & ISC_CBC_BRIGHT_MASK; + break; + case V4L2_CID_CONTRAST: + ctrls->contrast = ctrl->val & ISC_CBC_CONTRAST_MASK; + break; + case V4L2_CID_GAMMA: + ctrls->gamma_index = ctrl->val; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops isc_ctrl_ops = { + .s_ctrl = isc_s_ctrl, +}; + +static int isc_s_awb_ctrl(struct v4l2_ctrl *ctrl) +{ + struct isc_device *isc = container_of(ctrl->handler, + struct isc_device, ctrls.handler); + struct isc_ctrls *ctrls = &isc->ctrls; + + if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) + return 0; + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + if (ctrl->val == 1) + ctrls->awb = ISC_WB_AUTO; + else + ctrls->awb = ISC_WB_NONE; + + /* configure the controls with new values from v4l2 */ + if (ctrl->cluster[ISC_CTRL_R_GAIN]->is_new) + ctrls->gain[ISC_HIS_CFG_MODE_R] = isc->r_gain_ctrl->val; + if (ctrl->cluster[ISC_CTRL_B_GAIN]->is_new) + ctrls->gain[ISC_HIS_CFG_MODE_B] = isc->b_gain_ctrl->val; + if (ctrl->cluster[ISC_CTRL_GR_GAIN]->is_new) + ctrls->gain[ISC_HIS_CFG_MODE_GR] = isc->gr_gain_ctrl->val; + if (ctrl->cluster[ISC_CTRL_GB_GAIN]->is_new) + 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->r_off_ctrl->val; + if (ctrl->cluster[ISC_CTRL_B_OFF]->is_new) + 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->gr_off_ctrl->val; + if (ctrl->cluster[ISC_CTRL_GB_OFF]->is_new) + ctrls->offset[ISC_HIS_CFG_MODE_GB] = isc->gb_off_ctrl->val; + + isc_update_awb_ctrls(isc); + + mutex_lock(&isc->awb_mutex); + if (vb2_is_streaming(&isc->vb2_vidq)) { + /* + * If we are streaming, we can update profile to + * have the new settings in place. + */ + isc_update_profile(isc); + } else { + /* + * The auto cluster will activate automatically this + * control. This has to be deactivated when not + * streaming. + */ + v4l2_ctrl_activate(isc->do_wb_ctrl, false); + } + mutex_unlock(&isc->awb_mutex); + + /* if we have autowhitebalance on, start histogram procedure */ + if (ctrls->awb == ISC_WB_AUTO && + vb2_is_streaming(&isc->vb2_vidq) && + ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) + isc_set_histogram(isc, true); + + /* + * for one time whitebalance adjustment, check the button, + * if it's pressed, perform the one time operation. + */ + if (ctrls->awb == ISC_WB_NONE && + ctrl->cluster[ISC_CTRL_DO_WB]->is_new && + !(ctrl->cluster[ISC_CTRL_DO_WB]->flags & + V4L2_CTRL_FLAG_INACTIVE)) { + ctrls->awb = ISC_WB_ONETIME; + isc_set_histogram(isc, true); + v4l2_dbg(1, debug, &isc->v4l2_dev, + "One time white-balance started.\n"); + } + return 0; + } + return 0; +} + +static int isc_g_volatile_awb_ctrl(struct v4l2_ctrl *ctrl) +{ + struct isc_device *isc = container_of(ctrl->handler, + struct isc_device, ctrls.handler); + struct isc_ctrls *ctrls = &isc->ctrls; + + switch (ctrl->id) { + /* being a cluster, this id will be called for every control */ + case V4L2_CID_AUTO_WHITE_BALANCE: + ctrl->cluster[ISC_CTRL_R_GAIN]->val = + ctrls->gain[ISC_HIS_CFG_MODE_R]; + ctrl->cluster[ISC_CTRL_B_GAIN]->val = + ctrls->gain[ISC_HIS_CFG_MODE_B]; + ctrl->cluster[ISC_CTRL_GR_GAIN]->val = + ctrls->gain[ISC_HIS_CFG_MODE_GR]; + ctrl->cluster[ISC_CTRL_GB_GAIN]->val = + ctrls->gain[ISC_HIS_CFG_MODE_GB]; + + ctrl->cluster[ISC_CTRL_R_OFF]->val = + ctrls->offset[ISC_HIS_CFG_MODE_R]; + ctrl->cluster[ISC_CTRL_B_OFF]->val = + ctrls->offset[ISC_HIS_CFG_MODE_B]; + ctrl->cluster[ISC_CTRL_GR_OFF]->val = + ctrls->offset[ISC_HIS_CFG_MODE_GR]; + ctrl->cluster[ISC_CTRL_GB_OFF]->val = + ctrls->offset[ISC_HIS_CFG_MODE_GB]; + break; + } + return 0; +} + +static const struct v4l2_ctrl_ops isc_awb_ops = { + .s_ctrl = isc_s_awb_ctrl, + .g_volatile_ctrl = isc_g_volatile_awb_ctrl, +}; + +#define ISC_CTRL_OFF(_name, _id, _name_str) \ + static const struct v4l2_ctrl_config _name = { \ + .ops = &isc_awb_ops, \ + .id = _id, \ + .name = _name_str, \ + .type = V4L2_CTRL_TYPE_INTEGER, \ + .flags = V4L2_CTRL_FLAG_SLIDER, \ + .min = -4095, \ + .max = 4095, \ + .step = 1, \ + .def = 0, \ + } + +ISC_CTRL_OFF(isc_r_off_ctrl, ISC_CID_R_OFFSET, "Red Component Offset"); +ISC_CTRL_OFF(isc_b_off_ctrl, ISC_CID_B_OFFSET, "Blue Component Offset"); +ISC_CTRL_OFF(isc_gr_off_ctrl, ISC_CID_GR_OFFSET, "Green Red Component Offset"); +ISC_CTRL_OFF(isc_gb_off_ctrl, ISC_CID_GB_OFFSET, "Green Blue Component Offset"); + +#define ISC_CTRL_GAIN(_name, _id, _name_str) \ + static const struct v4l2_ctrl_config _name = { \ + .ops = &isc_awb_ops, \ + .id = _id, \ + .name = _name_str, \ + .type = V4L2_CTRL_TYPE_INTEGER, \ + .flags = V4L2_CTRL_FLAG_SLIDER, \ + .min = 0, \ + .max = 8191, \ + .step = 1, \ + .def = 512, \ + } + +ISC_CTRL_GAIN(isc_r_gain_ctrl, ISC_CID_R_GAIN, "Red Component Gain"); +ISC_CTRL_GAIN(isc_b_gain_ctrl, ISC_CID_B_GAIN, "Blue Component Gain"); +ISC_CTRL_GAIN(isc_gr_gain_ctrl, ISC_CID_GR_GAIN, "Green Red Component Gain"); +ISC_CTRL_GAIN(isc_gb_gain_ctrl, ISC_CID_GB_GAIN, "Green Blue Component Gain"); + +static int isc_ctrl_init(struct isc_device *isc) +{ + const struct v4l2_ctrl_ops *ops = &isc_ctrl_ops; + struct isc_ctrls *ctrls = &isc->ctrls; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; + int ret; + + ctrls->hist_stat = HIST_INIT; + isc_reset_awb_ctrls(isc); + + ret = v4l2_ctrl_handler_init(hdl, 13); + if (ret < 0) + return ret; + + /* Initialize product specific controls. For example, contrast */ + isc->config_ctrls(isc, ops); + + ctrls->brightness = 0; + + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -1024, 1023, 1, 0); + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, isc->gamma_max, 1, + isc->gamma_max); + isc->awb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops, + V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, 1); + + /* do_white_balance is a button, so min,max,step,default are ignored */ + isc->do_wb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops, + V4L2_CID_DO_WHITE_BALANCE, + 0, 0, 0, 0); + + if (!isc->do_wb_ctrl) { + ret = hdl->error; + v4l2_ctrl_handler_free(hdl); + return ret; + } + + v4l2_ctrl_activate(isc->do_wb_ctrl, false); + + isc->r_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_gain_ctrl, NULL); + isc->b_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_gain_ctrl, NULL); + isc->gr_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_gain_ctrl, NULL); + isc->gb_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_gain_ctrl, NULL); + isc->r_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_off_ctrl, NULL); + isc->b_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_off_ctrl, NULL); + isc->gr_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_off_ctrl, NULL); + isc->gb_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_off_ctrl, NULL); + + /* + * The cluster is in auto mode with autowhitebalance enabled + * and manual mode otherwise. + */ + v4l2_ctrl_auto_cluster(10, &isc->awb_ctrl, 0, true); + + v4l2_ctrl_handler_setup(hdl); + + return 0; +} + +static int isc_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct isc_device *isc = container_of(notifier->v4l2_dev, + struct isc_device, v4l2_dev); + struct isc_subdev_entity *subdev_entity = + container_of(notifier, struct isc_subdev_entity, notifier); + + if (video_is_registered(&isc->video_dev)) { + v4l2_err(&isc->v4l2_dev, "only supports one sub-device.\n"); + return -EBUSY; + } + + subdev_entity->sd = subdev; + + return 0; +} + +static void isc_async_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct isc_device *isc = container_of(notifier->v4l2_dev, + struct isc_device, v4l2_dev); + mutex_destroy(&isc->awb_mutex); + cancel_work_sync(&isc->awb_work); + video_unregister_device(&isc->video_dev); + v4l2_ctrl_handler_free(&isc->ctrls.handler); +} + +static struct isc_format *find_format_by_code(struct isc_device *isc, + unsigned int code, int *index) +{ + struct isc_format *fmt = &isc->formats_list[0]; + unsigned int i; + + for (i = 0; i < isc->formats_list_size; i++) { + if (fmt->mbus_code == code) { + *index = i; + return fmt; + } + + fmt++; + } + + return NULL; +} + +static int isc_formats_init(struct isc_device *isc) +{ + struct isc_format *fmt; + struct v4l2_subdev *subdev = isc->current_subdev->sd; + unsigned int num_fmts, i, j; + u32 list_size = isc->formats_list_size; + struct v4l2_subdev_mbus_code_enum mbus_code = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + num_fmts = 0; + while (!v4l2_subdev_call(subdev, pad, enum_mbus_code, + NULL, &mbus_code)) { + mbus_code.index++; + + fmt = find_format_by_code(isc, mbus_code.code, &i); + if (!fmt) { + v4l2_warn(&isc->v4l2_dev, "Mbus code %x not supported\n", + mbus_code.code); + continue; + } + + fmt->sd_support = true; + num_fmts++; + } + + if (!num_fmts) + return -ENXIO; + + isc->num_user_formats = num_fmts; + isc->user_formats = devm_kcalloc(isc->dev, + num_fmts, sizeof(*isc->user_formats), + GFP_KERNEL); + if (!isc->user_formats) + return -ENOMEM; + + fmt = &isc->formats_list[0]; + for (i = 0, j = 0; i < list_size; i++) { + if (fmt->sd_support) + isc->user_formats[j++] = fmt; + fmt++; + } + + return 0; +} + +static int isc_set_default_fmt(struct isc_device *isc) +{ + struct v4l2_format f = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .fmt.pix = { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .field = V4L2_FIELD_NONE, + .pixelformat = isc->user_formats[0]->fourcc, + }, + }; + int ret; + + ret = isc_try_fmt(isc, &f, NULL); + if (ret) + return ret; + + isc->fmt = f; + return 0; +} + +static int isc_async_complete(struct v4l2_async_notifier *notifier) +{ + struct isc_device *isc = container_of(notifier->v4l2_dev, + struct isc_device, v4l2_dev); + struct video_device *vdev = &isc->video_dev; + struct vb2_queue *q = &isc->vb2_vidq; + int ret = 0; + + INIT_WORK(&isc->awb_work, isc_awb_work); + + ret = v4l2_device_register_subdev_nodes(&isc->v4l2_dev); + if (ret < 0) { + v4l2_err(&isc->v4l2_dev, "Failed to register subdev nodes\n"); + return ret; + } + + isc->current_subdev = container_of(notifier, + struct isc_subdev_entity, notifier); + mutex_init(&isc->lock); + mutex_init(&isc->awb_mutex); + + init_completion(&isc->comp); + + /* Initialize videobuf2 queue */ + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + q->drv_priv = isc; + q->buf_struct_size = sizeof(struct isc_buffer); + q->ops = &isc_vb2_ops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &isc->lock; + q->min_buffers_needed = 1; + q->dev = isc->dev; + + ret = vb2_queue_init(q); + if (ret < 0) { + v4l2_err(&isc->v4l2_dev, + "vb2_queue_init() failed: %d\n", ret); + goto isc_async_complete_err; + } + + /* Init video dma queues */ + INIT_LIST_HEAD(&isc->dma_queue); + spin_lock_init(&isc->dma_queue_lock); + spin_lock_init(&isc->awb_lock); + + ret = isc_formats_init(isc); + if (ret < 0) { + v4l2_err(&isc->v4l2_dev, + "Init format failed: %d\n", ret); + goto isc_async_complete_err; + } + + ret = isc_set_default_fmt(isc); + if (ret) { + v4l2_err(&isc->v4l2_dev, "Could not set default format\n"); + goto isc_async_complete_err; + } + + ret = isc_ctrl_init(isc); + if (ret) { + v4l2_err(&isc->v4l2_dev, "Init isc ctrols failed: %d\n", ret); + goto isc_async_complete_err; + } + + /* Register video device */ + strscpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name)); + vdev->release = video_device_release_empty; + vdev->fops = &isc_fops; + vdev->ioctl_ops = &isc_ioctl_ops; + vdev->v4l2_dev = &isc->v4l2_dev; + vdev->vfl_dir = VFL_DIR_RX; + vdev->queue = q; + vdev->lock = &isc->lock; + vdev->ctrl_handler = &isc->ctrls.handler; + vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; + video_set_drvdata(vdev, isc); + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret < 0) { + v4l2_err(&isc->v4l2_dev, + "video_register_device failed: %d\n", ret); + goto isc_async_complete_err; + } + + return 0; + +isc_async_complete_err: + mutex_destroy(&isc->awb_mutex); + mutex_destroy(&isc->lock); + return ret; +} + +const struct v4l2_async_notifier_operations atmel_isc_async_ops = { + .bound = isc_async_bound, + .unbind = isc_async_unbind, + .complete = isc_async_complete, +}; +EXPORT_SYMBOL_GPL(atmel_isc_async_ops); + +void atmel_isc_subdev_cleanup(struct isc_device *isc) +{ + struct isc_subdev_entity *subdev_entity; + + list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { + v4l2_async_nf_unregister(&subdev_entity->notifier); + v4l2_async_nf_cleanup(&subdev_entity->notifier); + } + + INIT_LIST_HEAD(&isc->subdev_entities); +} +EXPORT_SYMBOL_GPL(atmel_isc_subdev_cleanup); + +int atmel_isc_pipeline_init(struct isc_device *isc) +{ + struct device *dev = isc->dev; + struct regmap *regmap = isc->regmap; + struct regmap_field *regs; + unsigned int i; + + /* + * DPCEN-->GDCEN-->BLCEN-->WB-->CFA-->CC--> + * GAM-->VHXS-->CSC-->CBC-->SUB422-->SUB420 + */ + const struct reg_field regfields[ISC_PIPE_LINE_NODE_NUM] = { + REG_FIELD(ISC_DPC_CTRL, 0, 0), + REG_FIELD(ISC_DPC_CTRL, 1, 1), + REG_FIELD(ISC_DPC_CTRL, 2, 2), + REG_FIELD(ISC_WB_CTRL, 0, 0), + REG_FIELD(ISC_CFA_CTRL, 0, 0), + REG_FIELD(ISC_CC_CTRL, 0, 0), + REG_FIELD(ISC_GAM_CTRL, 0, 0), + REG_FIELD(ISC_GAM_CTRL, 1, 1), + REG_FIELD(ISC_GAM_CTRL, 2, 2), + REG_FIELD(ISC_GAM_CTRL, 3, 3), + REG_FIELD(ISC_VHXS_CTRL, 0, 0), + REG_FIELD(ISC_CSC_CTRL + isc->offsets.csc, 0, 0), + REG_FIELD(ISC_CBC_CTRL + isc->offsets.cbc, 0, 0), + REG_FIELD(ISC_SUB422_CTRL + isc->offsets.sub422, 0, 0), + REG_FIELD(ISC_SUB420_CTRL + isc->offsets.sub420, 0, 0), + }; + + for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) { + regs = devm_regmap_field_alloc(dev, regmap, regfields[i]); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + isc->pipeline[i] = regs; + } + + return 0; +} +EXPORT_SYMBOL_GPL(atmel_isc_pipeline_init); + +/* regmap configuration */ +#define ATMEL_ISC_REG_MAX 0xd5c +const struct regmap_config atmel_isc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = ATMEL_ISC_REG_MAX, +}; +EXPORT_SYMBOL_GPL(atmel_isc_regmap_config); + +MODULE_AUTHOR("Songjun Wu"); +MODULE_AUTHOR("Eugen Hristev"); +MODULE_DESCRIPTION("Atmel ISC common code base"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc-clk.c b/drivers/staging/media/deprecated/atmel/atmel-isc-clk.c new file mode 100644 index 000000000000..d442b5f4c931 --- /dev/null +++ b/drivers/staging/media/deprecated/atmel/atmel-isc-clk.c @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Microchip Image Sensor Controller (ISC) common clock driver setup + * + * Copyright (C) 2016 Microchip Technology, Inc. + * + * Author: Songjun Wu + * Author: Eugen Hristev <eugen.hristev@microchip.com> + * + */ +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> + +#include "atmel-isc-regs.h" +#include "atmel-isc.h" + +static int isc_wait_clk_stable(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + struct regmap *regmap = isc_clk->regmap; + unsigned long timeout = jiffies + usecs_to_jiffies(1000); + unsigned int status; + + while (time_before(jiffies, timeout)) { + regmap_read(regmap, ISC_CLKSR, &status); + if (!(status & ISC_CLKSR_SIP)) + return 0; + + usleep_range(10, 250); + } + + return -ETIMEDOUT; +} + +static int isc_clk_prepare(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + int ret; + + ret = pm_runtime_resume_and_get(isc_clk->dev); + if (ret < 0) + return ret; + + return isc_wait_clk_stable(hw); +} + +static void isc_clk_unprepare(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + + isc_wait_clk_stable(hw); + + pm_runtime_put_sync(isc_clk->dev); +} + +static int isc_clk_enable(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + u32 id = isc_clk->id; + struct regmap *regmap = isc_clk->regmap; + unsigned long flags; + unsigned int status; + + dev_dbg(isc_clk->dev, "ISC CLK: %s, id = %d, div = %d, parent id = %d\n", + __func__, id, isc_clk->div, isc_clk->parent_id); + + spin_lock_irqsave(&isc_clk->lock, flags); + regmap_update_bits(regmap, ISC_CLKCFG, + ISC_CLKCFG_DIV_MASK(id) | ISC_CLKCFG_SEL_MASK(id), + (isc_clk->div << ISC_CLKCFG_DIV_SHIFT(id)) | + (isc_clk->parent_id << ISC_CLKCFG_SEL_SHIFT(id))); + + regmap_write(regmap, ISC_CLKEN, ISC_CLK(id)); + spin_unlock_irqrestore(&isc_clk->lock, flags); + + regmap_read(regmap, ISC_CLKSR, &status); + if (status & ISC_CLK(id)) + return 0; + else + return -EINVAL; +} + +static void isc_clk_disable(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + u32 id = isc_clk->id; + unsigned long flags; + + spin_lock_irqsave(&isc_clk->lock, flags); + regmap_write(isc_clk->regmap, ISC_CLKDIS, ISC_CLK(id)); + spin_unlock_irqrestore(&isc_clk->lock, flags); +} + +static int isc_clk_is_enabled(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + u32 status; + int ret; + + ret = pm_runtime_resume_and_get(isc_clk->dev); + if (ret < 0) + return 0; + + regmap_read(isc_clk->regmap, ISC_CLKSR, &status); + + pm_runtime_put_sync(isc_clk->dev); + + return status & ISC_CLK(isc_clk->id) ? 1 : 0; +} + +static unsigned long +isc_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + + return DIV_ROUND_CLOSEST(parent_rate, isc_clk->div + 1); +} + +static int isc_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + long best_rate = -EINVAL; + int best_diff = -1; + unsigned int i, div; + + for (i = 0; i < clk_hw_get_num_parents(hw); i++) { + struct clk_hw *parent; + unsigned long parent_rate; + + parent = clk_hw_get_parent_by_index(hw, i); + if (!parent) + continue; + + parent_rate = clk_hw_get_rate(parent); + if (!parent_rate) + continue; + + for (div = 1; div < ISC_CLK_MAX_DIV + 2; div++) { + unsigned long rate; + int diff; + + rate = DIV_ROUND_CLOSEST(parent_rate, div); + diff = abs(req->rate - rate); + + if (best_diff < 0 || best_diff > diff) { + best_rate = rate; + best_diff = diff; + req->best_parent_rate = parent_rate; + req->best_parent_hw = parent; + } + + if (!best_diff || rate < req->rate) + break; + } + + if (!best_diff) + break; + } + + dev_dbg(isc_clk->dev, + "ISC CLK: %s, best_rate = %ld, parent clk: %s @ %ld\n", + __func__, best_rate, + __clk_get_name((req->best_parent_hw)->clk), + req->best_parent_rate); + + if (best_rate < 0) + return best_rate; + + req->rate = best_rate; + + return 0; +} + +static int isc_clk_set_parent(struct clk_hw *hw, u8 index) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + + if (index >= clk_hw_get_num_parents(hw)) + return -EINVAL; + + isc_clk->parent_id = index; + + return 0; +} + +static u8 isc_clk_get_parent(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + + return isc_clk->parent_id; +} + +static int isc_clk_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + u32 div; + + if (!rate) + return -EINVAL; + + div = DIV_ROUND_CLOSEST(parent_rate, rate); + if (div > (ISC_CLK_MAX_DIV + 1) || !div) + return -EINVAL; + + isc_clk->div = div - 1; + + return 0; +} + +static const struct clk_ops isc_clk_ops = { + .prepare = isc_clk_prepare, + .unprepare = isc_clk_unprepare, + .enable = isc_clk_enable, + .disable = isc_clk_disable, + .is_enabled = isc_clk_is_enabled, + .recalc_rate = isc_clk_recalc_rate, + .determine_rate = isc_clk_determine_rate, + .set_parent = isc_clk_set_parent, + .get_parent = isc_clk_get_parent, + .set_rate = isc_clk_set_rate, +}; + +static int isc_clk_register(struct isc_device *isc, unsigned int id) +{ + struct regmap *regmap = isc->regmap; + struct device_node *np = isc->dev->of_node; + struct isc_clk *isc_clk; + struct clk_init_data init; + const char *clk_name = np->name; + const char *parent_names[3]; + int num_parents; + + if (id == ISC_ISPCK && !isc->ispck_required) + return 0; + + num_parents = of_clk_get_parent_count(np); + if (num_parents < 1 || num_parents > 3) + return -EINVAL; + + if (num_parents > 2 && id == ISC_ISPCK) + num_parents = 2; + + of_clk_parent_fill(np, parent_names, num_parents); + + if (id == ISC_MCK) + of_property_read_string(np, "clock-output-names", &clk_name); + else + clk_name = "isc-ispck"; + + init.parent_names = parent_names; + init.num_parents = num_parents; + init.name = clk_name; + init.ops = &isc_clk_ops; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; + + isc_clk = &isc->isc_clks[id]; + isc_clk->hw.init = &init; + isc_clk->regmap = regmap; + isc_clk->id = id; + isc_clk->dev = isc->dev; + spin_lock_init(&isc_clk->lock); + + isc_clk->clk = clk_register(isc->dev, &isc_clk->hw); + if (IS_ERR(isc_clk->clk)) { + dev_err(isc->dev, "%s: clock register fail\n", clk_name); + return PTR_ERR(isc_clk->clk); + } else if (id == ISC_MCK) { + of_clk_add_provider(np, of_clk_src_simple_get, isc_clk->clk); + } + + return 0; +} + +int atmel_isc_clk_init(struct isc_device *isc) +{ + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) + isc->isc_clks[i].clk = ERR_PTR(-EINVAL); + + for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) { + ret = isc_clk_register(isc, i); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(atmel_isc_clk_init); + +void atmel_isc_clk_cleanup(struct isc_device *isc) +{ + unsigned int i; + + of_clk_del_provider(isc->dev->of_node); + + for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) { + struct isc_clk *isc_clk = &isc->isc_clks[i]; + + if (!IS_ERR(isc_clk->clk)) + clk_unregister(isc_clk->clk); + } +} +EXPORT_SYMBOL_GPL(atmel_isc_clk_cleanup); diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc-regs.h b/drivers/staging/media/deprecated/atmel/atmel-isc-regs.h new file mode 100644 index 000000000000..d06b72228d4f --- /dev/null +++ b/drivers/staging/media/deprecated/atmel/atmel-isc-regs.h @@ -0,0 +1,413 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ATMEL_ISC_REGS_H +#define __ATMEL_ISC_REGS_H + +#include <linux/bitops.h> + +/* ISC Control Enable Register 0 */ +#define ISC_CTRLEN 0x00000000 + +/* ISC Control Disable Register 0 */ +#define ISC_CTRLDIS 0x00000004 + +/* ISC Control Status Register 0 */ +#define ISC_CTRLSR 0x00000008 + +#define ISC_CTRL_CAPTURE BIT(0) +#define ISC_CTRL_UPPRO BIT(1) +#define ISC_CTRL_HISREQ BIT(2) +#define ISC_CTRL_HISCLR BIT(3) + +/* ISC Parallel Front End Configuration 0 Register */ +#define ISC_PFE_CFG0 0x0000000c + +#define ISC_PFE_CFG0_HPOL_LOW BIT(0) +#define ISC_PFE_CFG0_VPOL_LOW BIT(1) +#define ISC_PFE_CFG0_PPOL_LOW BIT(2) +#define ISC_PFE_CFG0_CCIR656 BIT(9) +#define ISC_PFE_CFG0_CCIR_CRC BIT(10) +#define ISC_PFE_CFG0_MIPI BIT(14) + +#define ISC_PFE_CFG0_MODE_PROGRESSIVE (0x0 << 4) +#define ISC_PFE_CFG0_MODE_MASK GENMASK(6, 4) + +#define ISC_PFE_CFG0_BPS_EIGHT (0x4 << 28) +#define ISC_PFG_CFG0_BPS_NINE (0x3 << 28) +#define ISC_PFG_CFG0_BPS_TEN (0x2 << 28) +#define ISC_PFG_CFG0_BPS_ELEVEN (0x1 << 28) +#define ISC_PFG_CFG0_BPS_TWELVE (0x0 << 28) +#define ISC_PFE_CFG0_BPS_MASK GENMASK(30, 28) + +#define ISC_PFE_CFG0_COLEN BIT(12) +#define ISC_PFE_CFG0_ROWEN BIT(13) + +/* ISC Parallel Front End Configuration 1 Register */ +#define ISC_PFE_CFG1 0x00000010 + +#define ISC_PFE_CFG1_COLMIN(v) ((v)) +#define ISC_PFE_CFG1_COLMIN_MASK GENMASK(15, 0) +#define ISC_PFE_CFG1_COLMAX(v) ((v) << 16) +#define ISC_PFE_CFG1_COLMAX_MASK GENMASK(31, 16) + +/* ISC Parallel Front End Configuration 2 Register */ +#define ISC_PFE_CFG2 0x00000014 + +#define ISC_PFE_CFG2_ROWMIN(v) ((v)) +#define ISC_PFE_CFG2_ROWMIN_MASK GENMASK(15, 0) +#define ISC_PFE_CFG2_ROWMAX(v) ((v) << 16) +#define ISC_PFE_CFG2_ROWMAX_MASK GENMASK(31, 16) + +/* ISC Clock Enable Register */ +#define ISC_CLKEN 0x00000018 + +/* ISC Clock Disable Register */ +#define ISC_CLKDIS 0x0000001c + +/* ISC Clock Status Register */ +#define ISC_CLKSR 0x00000020 +#define ISC_CLKSR_SIP BIT(31) + +#define ISC_CLK(n) BIT(n) + +/* ISC Clock Configuration Register */ +#define ISC_CLKCFG 0x00000024 +#define ISC_CLKCFG_DIV_SHIFT(n) ((n)*16) +#define ISC_CLKCFG_DIV_MASK(n) GENMASK(((n)*16 + 7), (n)*16) +#define ISC_CLKCFG_SEL_SHIFT(n) ((n)*16 + 8) +#define ISC_CLKCFG_SEL_MASK(n) GENMASK(((n)*17 + 8), ((n)*16 + 8)) + +/* ISC Interrupt Enable Register */ +#define ISC_INTEN 0x00000028 + +/* ISC Interrupt Disable Register */ +#define ISC_INTDIS 0x0000002c + +/* ISC Interrupt Mask Register */ +#define ISC_INTMASK 0x00000030 + +/* ISC Interrupt Status Register */ +#define ISC_INTSR 0x00000034 + +#define ISC_INT_DDONE BIT(8) +#define ISC_INT_HISDONE BIT(12) + +/* ISC DPC Control Register */ +#define ISC_DPC_CTRL 0x40 + +#define ISC_DPC_CTRL_DPCEN BIT(0) +#define ISC_DPC_CTRL_GDCEN BIT(1) +#define ISC_DPC_CTRL_BLCEN BIT(2) + +/* ISC DPC Config Register */ +#define ISC_DPC_CFG 0x44 + +#define ISC_DPC_CFG_BAYSEL_SHIFT 0 + +#define ISC_DPC_CFG_EITPOL BIT(4) + +#define ISC_DPC_CFG_TA_ENABLE BIT(14) +#define ISC_DPC_CFG_TC_ENABLE BIT(13) +#define ISC_DPC_CFG_TM_ENABLE BIT(12) + +#define ISC_DPC_CFG_RE_MODE BIT(17) + +#define ISC_DPC_CFG_GDCCLP_SHIFT 20 +#define ISC_DPC_CFG_GDCCLP_MASK GENMASK(22, 20) + +#define ISC_DPC_CFG_BLOFF_SHIFT 24 +#define ISC_DPC_CFG_BLOFF_MASK GENMASK(31, 24) + +#define ISC_DPC_CFG_BAYCFG_SHIFT 0 +#define ISC_DPC_CFG_BAYCFG_MASK GENMASK(1, 0) +/* ISC DPC Threshold Median Register */ +#define ISC_DPC_THRESHM 0x48 + +/* ISC DPC Threshold Closest Register */ +#define ISC_DPC_THRESHC 0x4C + +/* ISC DPC Threshold Average Register */ +#define ISC_DPC_THRESHA 0x50 + +/* ISC DPC STatus Register */ +#define ISC_DPC_SR 0x54 + +/* ISC White Balance Control Register */ +#define ISC_WB_CTRL 0x00000058 + +/* ISC White Balance Configuration Register */ +#define ISC_WB_CFG 0x0000005c + +/* ISC White Balance Offset for R, GR Register */ +#define ISC_WB_O_RGR 0x00000060 + +/* ISC White Balance Offset for B, GB Register */ +#define ISC_WB_O_BGB 0x00000064 + +/* ISC White Balance Gain for R, GR Register */ +#define ISC_WB_G_RGR 0x00000068 + +/* ISC White Balance Gain for B, GB Register */ +#define ISC_WB_G_BGB 0x0000006c + +/* ISC Color Filter Array Control Register */ +#define ISC_CFA_CTRL 0x00000070 + +/* ISC Color Filter Array Configuration Register */ +#define ISC_CFA_CFG 0x00000074 +#define ISC_CFA_CFG_EITPOL BIT(4) + +#define ISC_BAY_CFG_GRGR 0x0 +#define ISC_BAY_CFG_RGRG 0x1 +#define ISC_BAY_CFG_GBGB 0x2 +#define ISC_BAY_CFG_BGBG 0x3 + +/* ISC Color Correction Control Register */ +#define ISC_CC_CTRL 0x00000078 + +/* ISC Color Correction RR RG Register */ +#define ISC_CC_RR_RG 0x0000007c + +/* ISC Color Correction RB OR Register */ +#define ISC_CC_RB_OR 0x00000080 + +/* ISC Color Correction GR GG Register */ +#define ISC_CC_GR_GG 0x00000084 + +/* ISC Color Correction GB OG Register */ +#define ISC_CC_GB_OG 0x00000088 + +/* ISC Color Correction BR BG Register */ +#define ISC_CC_BR_BG 0x0000008c + +/* ISC Color Correction BB OB Register */ +#define ISC_CC_BB_OB 0x00000090 + +/* ISC Gamma Correction Control Register */ +#define ISC_GAM_CTRL 0x00000094 + +#define ISC_GAM_CTRL_BIPART BIT(4) + +/* ISC_Gamma Correction Blue Entry Register */ +#define ISC_GAM_BENTRY 0x00000098 + +/* ISC_Gamma Correction Green Entry Register */ +#define ISC_GAM_GENTRY 0x00000198 + +/* ISC_Gamma Correction Green Entry Register */ +#define ISC_GAM_RENTRY 0x00000298 + +/* ISC VHXS Control Register */ +#define ISC_VHXS_CTRL 0x398 + +/* ISC VHXS Source Size Register */ +#define ISC_VHXS_SS 0x39C + +/* ISC VHXS Destination Size Register */ +#define ISC_VHXS_DS 0x3A0 + +/* ISC Vertical Factor Register */ +#define ISC_VXS_FACT 0x3a4 + +/* ISC Horizontal Factor Register */ +#define ISC_HXS_FACT 0x3a8 + +/* ISC Vertical Config Register */ +#define ISC_VXS_CFG 0x3ac + +/* ISC Horizontal Config Register */ +#define ISC_HXS_CFG 0x3b0 + +/* ISC Vertical Tap Register */ +#define ISC_VXS_TAP 0x3b4 + +/* ISC Horizontal Tap Register */ +#define ISC_HXS_TAP 0x434 + +/* Offset for CSC register specific to sama5d2 product */ +#define ISC_SAMA5D2_CSC_OFFSET 0 +/* Offset for CSC register specific to sama7g5 product */ +#define ISC_SAMA7G5_CSC_OFFSET 0x11c + +/* Color Space Conversion Control Register */ +#define ISC_CSC_CTRL 0x00000398 + +/* Color Space Conversion YR YG Register */ +#define ISC_CSC_YR_YG 0x0000039c + +/* Color Space Conversion YB OY Register */ +#define ISC_CSC_YB_OY 0x000003a0 + +/* Color Space Conversion CBR CBG Register */ +#define ISC_CSC_CBR_CBG 0x000003a4 + +/* Color Space Conversion CBB OCB Register */ +#define ISC_CSC_CBB_OCB 0x000003a8 + +/* Color Space Conversion CRR CRG Register */ +#define ISC_CSC_CRR_CRG 0x000003ac + +/* Color Space Conversion CRB OCR Register */ +#define ISC_CSC_CRB_OCR 0x000003b0 + +/* Offset for CBC register specific to sama5d2 product */ +#define ISC_SAMA5D2_CBC_OFFSET 0 +/* Offset for CBC register specific to sama7g5 product */ +#define ISC_SAMA7G5_CBC_OFFSET 0x11c + +/* Contrast And Brightness Control Register */ +#define ISC_CBC_CTRL 0x000003b4 + +/* Contrast And Brightness Configuration Register */ +#define ISC_CBC_CFG 0x000003b8 + +/* Brightness Register */ +#define ISC_CBC_BRIGHT 0x000003bc +#define ISC_CBC_BRIGHT_MASK GENMASK(10, 0) + +/* Contrast Register */ +#define ISC_CBC_CONTRAST 0x000003c0 +#define ISC_CBC_CONTRAST_MASK GENMASK(11, 0) + +/* Hue Register */ +#define ISC_CBCHS_HUE 0x4e0 +/* Saturation Register */ +#define ISC_CBCHS_SAT 0x4e4 + +/* Offset for SUB422 register specific to sama5d2 product */ +#define ISC_SAMA5D2_SUB422_OFFSET 0 +/* Offset for SUB422 register specific to sama7g5 product */ +#define ISC_SAMA7G5_SUB422_OFFSET 0x124 + +/* Subsampling 4:4:4 to 4:2:2 Control Register */ +#define ISC_SUB422_CTRL 0x000003c4 + +/* Offset for SUB420 register specific to sama5d2 product */ +#define ISC_SAMA5D2_SUB420_OFFSET 0 +/* Offset for SUB420 register specific to sama7g5 product */ +#define ISC_SAMA7G5_SUB420_OFFSET 0x124 +/* Subsampling 4:2:2 to 4:2:0 Control Register */ +#define ISC_SUB420_CTRL 0x000003cc + +/* Offset for RLP register specific to sama5d2 product */ +#define ISC_SAMA5D2_RLP_OFFSET 0 +/* Offset for RLP register specific to sama7g5 product */ +#define ISC_SAMA7G5_RLP_OFFSET 0x124 +/* Rounding, Limiting and Packing Configuration Register */ +#define ISC_RLP_CFG 0x000003d0 + +#define ISC_RLP_CFG_MODE_DAT8 0x0 +#define ISC_RLP_CFG_MODE_DAT9 0x1 +#define ISC_RLP_CFG_MODE_DAT10 0x2 +#define ISC_RLP_CFG_MODE_DAT11 0x3 +#define ISC_RLP_CFG_MODE_DAT12 0x4 +#define ISC_RLP_CFG_MODE_DATY8 0x5 +#define ISC_RLP_CFG_MODE_DATY10 0x6 +#define ISC_RLP_CFG_MODE_ARGB444 0x7 +#define ISC_RLP_CFG_MODE_ARGB555 0x8 +#define ISC_RLP_CFG_MODE_RGB565 0x9 +#define ISC_RLP_CFG_MODE_ARGB32 0xa +#define ISC_RLP_CFG_MODE_YYCC 0xb +#define ISC_RLP_CFG_MODE_YYCC_LIMITED 0xc +#define ISC_RLP_CFG_MODE_YCYC 0xd +#define ISC_RLP_CFG_MODE_MASK GENMASK(3, 0) + +#define ISC_RLP_CFG_LSH BIT(5) + +#define ISC_RLP_CFG_YMODE_YUYV (3 << 6) +#define ISC_RLP_CFG_YMODE_YVYU (2 << 6) +#define ISC_RLP_CFG_YMODE_VYUY (0 << 6) +#define ISC_RLP_CFG_YMODE_UYVY (1 << 6) + +#define ISC_RLP_CFG_YMODE_MASK GENMASK(7, 6) + +/* Offset for HIS register specific to sama5d2 product */ +#define ISC_SAMA5D2_HIS_OFFSET 0 +/* Offset for HIS register specific to sama7g5 product */ +#define ISC_SAMA7G5_HIS_OFFSET 0x124 +/* Histogram Control Register */ +#define ISC_HIS_CTRL 0x000003d4 + +#define ISC_HIS_CTRL_EN BIT(0) +#define ISC_HIS_CTRL_DIS 0x0 + +/* Histogram Configuration Register */ +#define ISC_HIS_CFG 0x000003d8 + +#define ISC_HIS_CFG_MODE_GR 0x0 +#define ISC_HIS_CFG_MODE_R 0x1 +#define ISC_HIS_CFG_MODE_GB 0x2 +#define ISC_HIS_CFG_MODE_B 0x3 +#define ISC_HIS_CFG_MODE_Y 0x4 +#define ISC_HIS_CFG_MODE_RAW 0x5 +#define ISC_HIS_CFG_MODE_YCCIR656 0x6 + +#define ISC_HIS_CFG_BAYSEL_SHIFT 4 + +#define ISC_HIS_CFG_RAR BIT(8) + +/* Offset for DMA register specific to sama5d2 product */ +#define ISC_SAMA5D2_DMA_OFFSET 0 +/* Offset for DMA register specific to sama7g5 product */ +#define ISC_SAMA7G5_DMA_OFFSET 0x13c + +/* DMA Configuration Register */ +#define ISC_DCFG 0x000003e0 +#define ISC_DCFG_IMODE_PACKED8 0x0 +#define ISC_DCFG_IMODE_PACKED16 0x1 +#define ISC_DCFG_IMODE_PACKED32 0x2 +#define ISC_DCFG_IMODE_YC422SP 0x3 +#define ISC_DCFG_IMODE_YC422P 0x4 +#define ISC_DCFG_IMODE_YC420SP 0x5 +#define ISC_DCFG_IMODE_YC420P 0x6 +#define ISC_DCFG_IMODE_MASK GENMASK(2, 0) + +#define ISC_DCFG_YMBSIZE_SINGLE (0x0 << 4) +#define ISC_DCFG_YMBSIZE_BEATS4 (0x1 << 4) +#define ISC_DCFG_YMBSIZE_BEATS8 (0x2 << 4) +#define ISC_DCFG_YMBSIZE_BEATS16 (0x3 << 4) +#define ISC_DCFG_YMBSIZE_BEATS32 (0x4 << 4) +#define ISC_DCFG_YMBSIZE_MASK GENMASK(6, 4) + +#define ISC_DCFG_CMBSIZE_SINGLE (0x0 << 8) +#define ISC_DCFG_CMBSIZE_BEATS4 (0x1 << 8) +#define ISC_DCFG_CMBSIZE_BEATS8 (0x2 << 8) +#define ISC_DCFG_CMBSIZE_BEATS16 (0x3 << 8) +#define ISC_DCFG_CMBSIZE_BEATS32 (0x4 << 8) +#define ISC_DCFG_CMBSIZE_MASK GENMASK(10, 8) + +/* DMA Control Register */ +#define ISC_DCTRL 0x000003e4 + +#define ISC_DCTRL_DVIEW_PACKED (0x0 << 1) +#define ISC_DCTRL_DVIEW_SEMIPLANAR (0x1 << 1) +#define ISC_DCTRL_DVIEW_PLANAR (0x2 << 1) +#define ISC_DCTRL_DVIEW_MASK GENMASK(2, 1) + +#define ISC_DCTRL_IE_IS (0x0 << 4) + +/* DMA Descriptor Address Register */ +#define ISC_DNDA 0x000003e8 + +/* DMA Address 0 Register */ +#define ISC_DAD0 0x000003ec + +/* DMA Address 1 Register */ +#define ISC_DAD1 0x000003f4 + +/* DMA Address 2 Register */ +#define ISC_DAD2 0x000003fc + +/* Offset for version register specific to sama5d2 product */ +#define ISC_SAMA5D2_VERSION_OFFSET 0 +#define ISC_SAMA7G5_VERSION_OFFSET 0x13c +/* Version Register */ +#define ISC_VERSION 0x0000040c + +/* Offset for version register specific to sama5d2 product */ +#define ISC_SAMA5D2_HIS_ENTRY_OFFSET 0 +/* Offset for version register specific to sama7g5 product */ +#define ISC_SAMA7G5_HIS_ENTRY_OFFSET 0x14c +/* Histogram Entry */ +#define ISC_HIS_ENTRY 0x00000410 + +#endif diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc.h b/drivers/staging/media/deprecated/atmel/atmel-isc.h new file mode 100644 index 000000000000..dfc030b5a08f --- /dev/null +++ b/drivers/staging/media/deprecated/atmel/atmel-isc.h @@ -0,0 +1,362 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Microchip Image Sensor Controller (ISC) driver header file + * + * Copyright (C) 2016-2019 Microchip Technology, Inc. + * + * Author: Songjun Wu + * Author: Eugen Hristev <eugen.hristev@microchip.com> + * + */ +#ifndef _ATMEL_ISC_H_ + +#include <linux/clk-provider.h> +#include <linux/platform_device.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/videobuf2-dma-contig.h> + +#define ISC_CLK_MAX_DIV 255 + +enum isc_clk_id { + ISC_ISPCK = 0, + ISC_MCK = 1, +}; + +struct isc_clk { + struct clk_hw hw; + struct clk *clk; + struct regmap *regmap; + spinlock_t lock; /* serialize access to clock registers */ + u8 id; + u8 parent_id; + u32 div; + struct device *dev; +}; + +#define to_isc_clk(v) container_of(v, struct isc_clk, hw) + +struct isc_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +struct isc_subdev_entity { + struct v4l2_subdev *sd; + struct v4l2_async_subdev *asd; + struct device_node *epn; + struct v4l2_async_notifier notifier; + + u32 pfe_cfg0; + + struct list_head list; +}; + +/* + * struct isc_format - ISC media bus format information + This structure represents the interface between the ISC + and the sensor. It's the input format received by + the ISC. + * @fourcc: Fourcc code for this format + * @mbus_code: V4L2 media bus format code. + * @cfa_baycfg: If this format is RAW BAYER, indicate the type of bayer. + this is either BGBG, RGRG, etc. + * @pfe_cfg0_bps: Number of hardware data lines connected to the ISC + */ + +struct isc_format { + u32 fourcc; + u32 mbus_code; + u32 cfa_baycfg; + + bool sd_support; + u32 pfe_cfg0_bps; +}; + +/* Pipeline bitmap */ +#define DPC_DPCENABLE BIT(0) +#define DPC_GDCENABLE BIT(1) +#define DPC_BLCENABLE BIT(2) +#define WB_ENABLE BIT(3) +#define CFA_ENABLE BIT(4) +#define CC_ENABLE BIT(5) +#define GAM_ENABLE BIT(6) +#define GAM_BENABLE BIT(7) +#define GAM_GENABLE BIT(8) +#define GAM_RENABLE BIT(9) +#define VHXS_ENABLE BIT(10) +#define CSC_ENABLE BIT(11) +#define CBC_ENABLE BIT(12) +#define SUB422_ENABLE BIT(13) +#define SUB420_ENABLE BIT(14) + +#define GAM_ENABLES (GAM_RENABLE | GAM_GENABLE | GAM_BENABLE | GAM_ENABLE) + +/* + * struct fmt_config - ISC format configuration and internal pipeline + This structure represents the internal configuration + of the ISC. + It also holds the format that ISC will present to v4l2. + * @sd_format: Pointer to an isc_format struct that holds the sensor + configuration. + * @fourcc: Fourcc code for this format. + * @bpp: Bytes per pixel in the current format. + * @bpp_v4l2: Bytes per pixel in the current format, for v4l2. + This differs from 'bpp' in the sense that in planar + formats, it refers only to the first plane. + * @rlp_cfg_mode: Configuration of the RLP (rounding, limiting packaging) + * @dcfg_imode: Configuration of the input of the DMA module + * @dctrl_dview: Configuration of the output of the DMA module + * @bits_pipeline: Configuration of the pipeline, which modules are enabled + */ +struct fmt_config { + struct isc_format *sd_format; + + u32 fourcc; + u8 bpp; + u8 bpp_v4l2; + + u32 rlp_cfg_mode; + u32 dcfg_imode; + u32 dctrl_dview; + + u32 bits_pipeline; +}; + +#define HIST_ENTRIES 512 +#define HIST_BAYER (ISC_HIS_CFG_MODE_B + 1) + +enum{ + HIST_INIT = 0, + HIST_ENABLED, + HIST_DISABLED, +}; + +struct isc_ctrls { + struct v4l2_ctrl_handler handler; + + u32 brightness; + u32 contrast; + u8 gamma_index; +#define ISC_WB_NONE 0 +#define ISC_WB_AUTO 1 +#define ISC_WB_ONETIME 2 + u8 awb; + + /* one for each component : GR, R, GB, B */ + u32 gain[HIST_BAYER]; + s32 offset[HIST_BAYER]; + + u32 hist_entry[HIST_ENTRIES]; + u32 hist_count[HIST_BAYER]; + u8 hist_id; + u8 hist_stat; +#define HIST_MIN_INDEX 0 +#define HIST_MAX_INDEX 1 + u32 hist_minmax[HIST_BAYER][2]; +}; + +#define ISC_PIPE_LINE_NODE_NUM 15 + +/* + * struct isc_reg_offsets - ISC device register offsets + * @csc: Offset for the CSC register + * @cbc: Offset for the CBC register + * @sub422: Offset for the SUB422 register + * @sub420: Offset for the SUB420 register + * @rlp: Offset for the RLP register + * @his: Offset for the HIS related registers + * @dma: Offset for the DMA related registers + * @version: Offset for the version register + * @his_entry: Offset for the HIS entries registers + */ +struct isc_reg_offsets { + u32 csc; + u32 cbc; + u32 sub422; + u32 sub420; + u32 rlp; + u32 his; + u32 dma; + u32 version; + u32 his_entry; +}; + +/* + * struct isc_device - ISC device driver data/config struct + * @regmap: Register map + * @hclock: Hclock clock input (refer datasheet) + * @ispck: iscpck clock (refer datasheet) + * @isc_clks: ISC clocks + * @ispck_required: ISC requires ISP Clock initialization + * @dcfg: DMA master configuration, architecture dependent + * + * @dev: Registered device driver + * @v4l2_dev: v4l2 registered device + * @video_dev: registered video device + * + * @vb2_vidq: video buffer 2 video queue + * @dma_queue_lock: lock to serialize the dma buffer queue + * @dma_queue: the queue for dma buffers + * @cur_frm: current isc frame/buffer + * @sequence: current frame number + * @stop: true if isc is not streaming, false if streaming + * @comp: completion reference that signals frame completion + * + * @fmt: current v42l format + * @user_formats: list of formats that are supported and agreed with sd + * @num_user_formats: how many formats are in user_formats + * + * @config: current ISC format configuration + * @try_config: the current ISC try format , not yet activated + * + * @ctrls: holds information about ISC controls + * @do_wb_ctrl: control regarding the DO_WHITE_BALANCE button + * @awb_work: workqueue reference for autowhitebalance histogram + * analysis + * + * @lock: lock for serializing userspace file operations + * with ISC operations + * @awb_mutex: serialize access to streaming status from awb work queue + * @awb_lock: lock for serializing awb work queue operations + * with DMA/buffer operations + * + * @pipeline: configuration of the ISC pipeline + * + * @current_subdev: current subdevice: the sensor + * @subdev_entities: list of subdevice entitites + * + * @gamma_table: pointer to the table with gamma values, has + * gamma_max sets of GAMMA_ENTRIES entries each + * @gamma_max: maximum number of sets of inside the gamma_table + * + * @max_width: maximum frame width, dependent on the internal RAM + * @max_height: maximum frame height, dependent on the internal RAM + * + * @config_dpc: pointer to a function that initializes product + * specific DPC module + * @config_csc: pointer to a function that initializes product + * specific CSC module + * @config_cbc: pointer to a function that initializes product + * specific CBC module + * @config_cc: pointer to a function that initializes product + * specific CC module + * @config_gam: pointer to a function that initializes product + * specific GAMMA module + * @config_rlp: pointer to a function that initializes product + * specific RLP module + * @config_ctrls: pointer to a functoin that initializes product + * specific v4l2 controls. + * + * @adapt_pipeline: pointer to a function that adapts the pipeline bits + * to the product specific pipeline + * + * @offsets: struct holding the product specific register offsets + * @controller_formats: pointer to the array of possible formats that the + * controller can output + * @formats_list: pointer to the array of possible formats that can + * be used as an input to the controller + * @controller_formats_size: size of controller_formats array + * @formats_list_size: size of formats_list array + */ +struct isc_device { + struct regmap *regmap; + struct clk *hclock; + struct clk *ispck; + struct isc_clk isc_clks[2]; + bool ispck_required; + u32 dcfg; + + struct device *dev; + struct v4l2_device v4l2_dev; + struct video_device video_dev; + + struct vb2_queue vb2_vidq; + spinlock_t dma_queue_lock; + struct list_head dma_queue; + struct isc_buffer *cur_frm; + unsigned int sequence; + bool stop; + struct completion comp; + + struct v4l2_format fmt; + struct isc_format **user_formats; + unsigned int num_user_formats; + + struct fmt_config config; + struct fmt_config try_config; + + struct isc_ctrls ctrls; + struct work_struct awb_work; + + struct mutex lock; + struct mutex awb_mutex; + spinlock_t awb_lock; + + struct regmap_field *pipeline[ISC_PIPE_LINE_NODE_NUM]; + + struct isc_subdev_entity *current_subdev; + struct list_head subdev_entities; + + struct { +#define ISC_CTRL_DO_WB 1 +#define ISC_CTRL_R_GAIN 2 +#define ISC_CTRL_B_GAIN 3 +#define ISC_CTRL_GR_GAIN 4 +#define ISC_CTRL_GB_GAIN 5 +#define ISC_CTRL_R_OFF 6 +#define ISC_CTRL_B_OFF 7 +#define ISC_CTRL_GR_OFF 8 +#define ISC_CTRL_GB_OFF 9 + struct v4l2_ctrl *awb_ctrl; + struct v4l2_ctrl *do_wb_ctrl; + struct v4l2_ctrl *r_gain_ctrl; + struct v4l2_ctrl *b_gain_ctrl; + struct v4l2_ctrl *gr_gain_ctrl; + struct v4l2_ctrl *gb_gain_ctrl; + struct v4l2_ctrl *r_off_ctrl; + struct v4l2_ctrl *b_off_ctrl; + struct v4l2_ctrl *gr_off_ctrl; + struct v4l2_ctrl *gb_off_ctrl; + }; + +#define GAMMA_ENTRIES 64 + /* pointer to the defined gamma table */ + const u32 (*gamma_table)[GAMMA_ENTRIES]; + u32 gamma_max; + + u32 max_width; + u32 max_height; + + struct { + void (*config_dpc)(struct isc_device *isc); + void (*config_csc)(struct isc_device *isc); + void (*config_cbc)(struct isc_device *isc); + void (*config_cc)(struct isc_device *isc); + void (*config_gam)(struct isc_device *isc); + void (*config_rlp)(struct isc_device *isc); + + void (*config_ctrls)(struct isc_device *isc, + const struct v4l2_ctrl_ops *ops); + + void (*adapt_pipeline)(struct isc_device *isc); + }; + + struct isc_reg_offsets offsets; + const struct isc_format *controller_formats; + struct isc_format *formats_list; + u32 controller_formats_size; + u32 formats_list_size; +}; + +extern const struct regmap_config atmel_isc_regmap_config; +extern const struct v4l2_async_notifier_operations atmel_isc_async_ops; + +irqreturn_t atmel_isc_interrupt(int irq, void *dev_id); +int atmel_isc_pipeline_init(struct isc_device *isc); +int atmel_isc_clk_init(struct isc_device *isc); +void atmel_isc_subdev_cleanup(struct isc_device *isc); +void atmel_isc_clk_cleanup(struct isc_device *isc); + +#endif diff --git a/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c b/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c new file mode 100644 index 000000000000..ba0614f981a2 --- /dev/null +++ b/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c @@ -0,0 +1,653 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip Image Sensor Controller (ISC) driver + * + * Copyright (C) 2016-2019 Microchip Technology, Inc. + * + * Author: Songjun Wu + * Author: Eugen Hristev <eugen.hristev@microchip.com> + * + * + * Sensor-->PFE-->WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB-->RLP-->DMA + * + * ISC video pipeline integrates the following submodules: + * PFE: Parallel Front End to sample the camera sensor input stream + * WB: Programmable white balance in the Bayer domain + * CFA: Color filter array interpolation module + * CC: Programmable color correction + * GAM: Gamma correction + * CSC: Programmable color space conversion + * CBC: Contrast and Brightness control + * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling + * RLP: This module performs rounding, range limiting + * and packing of the incoming data + */ + +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/math64.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/videodev2.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-image-sizes.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> +#include <media/videobuf2-dma-contig.h> + +#include "atmel-isc-regs.h" +#include "atmel-isc.h" + +#define ISC_SAMA5D2_MAX_SUPPORT_WIDTH 2592 +#define ISC_SAMA5D2_MAX_SUPPORT_HEIGHT 1944 + +#define ISC_SAMA5D2_PIPELINE \ + (WB_ENABLE | CFA_ENABLE | CC_ENABLE | GAM_ENABLES | CSC_ENABLE | \ + CBC_ENABLE | SUB422_ENABLE | SUB420_ENABLE) + +/* This is a list of the formats that the ISC can *output* */ +static const struct isc_format sama5d2_controller_formats[] = { + { + .fourcc = V4L2_PIX_FMT_ARGB444, + }, { + .fourcc = V4L2_PIX_FMT_ARGB555, + }, { + .fourcc = V4L2_PIX_FMT_RGB565, + }, { + .fourcc = V4L2_PIX_FMT_ABGR32, + }, { + .fourcc = V4L2_PIX_FMT_XBGR32, + }, { + .fourcc = V4L2_PIX_FMT_YUV420, + }, { + .fourcc = V4L2_PIX_FMT_YUYV, + }, { + .fourcc = V4L2_PIX_FMT_YUV422P, + }, { + .fourcc = V4L2_PIX_FMT_GREY, + }, { + .fourcc = V4L2_PIX_FMT_Y10, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR8, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG8, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG8, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB8, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR10, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG10, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG10, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB10, + }, +}; + +/* This is a list of formats that the ISC can receive as *input* */ +static struct isc_format sama5d2_formats_list[] = { + { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, + .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, + .cfa_baycfg = ISC_BAY_CFG_BGBG, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, + .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, + .cfa_baycfg = ISC_BAY_CFG_GBGB, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, + .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, + .cfa_baycfg = ISC_BAY_CFG_GRGR, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, + .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, + .cfa_baycfg = ISC_BAY_CFG_RGRG, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, + .cfa_baycfg = ISC_BAY_CFG_RGRG, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, + .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, + .cfa_baycfg = ISC_BAY_CFG_GBGB, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, + .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, + .cfa_baycfg = ISC_BAY_CFG_GRGR, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, + .cfa_baycfg = ISC_BAY_CFG_RGRG, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, + .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, + .cfa_baycfg = ISC_BAY_CFG_BGBG, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, + .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, + .cfa_baycfg = ISC_BAY_CFG_GBGB, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, + .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, + .cfa_baycfg = ISC_BAY_CFG_GRGR, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB12, + .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, + .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, + .cfa_baycfg = ISC_BAY_CFG_RGRG, + }, + { + .fourcc = V4L2_PIX_FMT_GREY, + .mbus_code = MEDIA_BUS_FMT_Y8_1X8, + .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, + }, + { + .fourcc = V4L2_PIX_FMT_YUYV, + .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, + .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, + }, + { + .fourcc = V4L2_PIX_FMT_RGB565, + .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, + }, + { + .fourcc = V4L2_PIX_FMT_Y10, + .mbus_code = MEDIA_BUS_FMT_Y10_1X10, + .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, + }, + +}; + +static void isc_sama5d2_config_csc(struct isc_device *isc) +{ + struct regmap *regmap = isc->regmap; + + /* Convert RGB to YUV */ + regmap_write(regmap, ISC_CSC_YR_YG + isc->offsets.csc, + 0x42 | (0x81 << 16)); + regmap_write(regmap, ISC_CSC_YB_OY + isc->offsets.csc, + 0x19 | (0x10 << 16)); + regmap_write(regmap, ISC_CSC_CBR_CBG + isc->offsets.csc, + 0xFDA | (0xFB6 << 16)); + regmap_write(regmap, ISC_CSC_CBB_OCB + isc->offsets.csc, + 0x70 | (0x80 << 16)); + regmap_write(regmap, ISC_CSC_CRR_CRG + isc->offsets.csc, + 0x70 | (0xFA2 << 16)); + regmap_write(regmap, ISC_CSC_CRB_OCR + isc->offsets.csc, + 0xFEE | (0x80 << 16)); +} + +static void isc_sama5d2_config_cbc(struct isc_device *isc) +{ + struct regmap *regmap = isc->regmap; + + regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc, + isc->ctrls.brightness); + regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc, + isc->ctrls.contrast); +} + +static void isc_sama5d2_config_cc(struct isc_device *isc) +{ + struct regmap *regmap = isc->regmap; + + /* Configure each register at the neutral fixed point 1.0 or 0.0 */ + regmap_write(regmap, ISC_CC_RR_RG, (1 << 8)); + regmap_write(regmap, ISC_CC_RB_OR, 0); + regmap_write(regmap, ISC_CC_GR_GG, (1 << 8) << 16); + regmap_write(regmap, ISC_CC_GB_OG, 0); + regmap_write(regmap, ISC_CC_BR_BG, 0); + regmap_write(regmap, ISC_CC_BB_OB, (1 << 8)); +} + +static void isc_sama5d2_config_ctrls(struct isc_device *isc, + const struct v4l2_ctrl_ops *ops) +{ + struct isc_ctrls *ctrls = &isc->ctrls; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; + + ctrls->contrast = 256; + + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 256); +} + +static void isc_sama5d2_config_dpc(struct isc_device *isc) +{ + /* This module is not present on sama5d2 pipeline */ +} + +static void isc_sama5d2_config_gam(struct isc_device *isc) +{ + /* No specific gamma configuration */ +} + +static void isc_sama5d2_config_rlp(struct isc_device *isc) +{ + struct regmap *regmap = isc->regmap; + u32 rlp_mode = isc->config.rlp_cfg_mode; + + /* + * In sama5d2, the YUV planar modes and the YUYV modes are treated + * in the same way in RLP register. + * Normally, YYCC mode should be Luma(n) - Color B(n) - Color R (n) + * and YCYC should be Luma(n + 1) - Color B (n) - Luma (n) - Color R (n) + * but in sama5d2, the YCYC mode does not exist, and YYCC must be + * selected for both planar and interleaved modes, as in fact + * both modes are supported. + * + * Thus, if the YCYC mode is selected, replace it with the + * sama5d2-compliant mode which is YYCC . + */ + if ((rlp_mode & ISC_RLP_CFG_MODE_MASK) == ISC_RLP_CFG_MODE_YCYC) { + rlp_mode &= ~ISC_RLP_CFG_MODE_MASK; + rlp_mode |= ISC_RLP_CFG_MODE_YYCC; + } + + regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp, + ISC_RLP_CFG_MODE_MASK, rlp_mode); +} + +static void isc_sama5d2_adapt_pipeline(struct isc_device *isc) +{ + isc->try_config.bits_pipeline &= ISC_SAMA5D2_PIPELINE; +} + +/* Gamma table with gamma 1/2.2 */ +static const u32 isc_sama5d2_gamma_table[][GAMMA_ENTRIES] = { + /* 0 --> gamma 1/1.8 */ + { 0x65, 0x66002F, 0x950025, 0xBB0020, 0xDB001D, 0xF8001A, + 0x1130018, 0x12B0017, 0x1420016, 0x1580014, 0x16D0013, 0x1810012, + 0x1940012, 0x1A60012, 0x1B80011, 0x1C90010, 0x1DA0010, 0x1EA000F, + 0x1FA000F, 0x209000F, 0x218000F, 0x227000E, 0x235000E, 0x243000E, + 0x251000E, 0x25F000D, 0x26C000D, 0x279000D, 0x286000D, 0x293000C, + 0x2A0000C, 0x2AC000C, 0x2B8000C, 0x2C4000C, 0x2D0000B, 0x2DC000B, + 0x2E7000B, 0x2F3000B, 0x2FE000B, 0x309000B, 0x314000B, 0x31F000A, + 0x32A000A, 0x334000B, 0x33F000A, 0x349000A, 0x354000A, 0x35E000A, + 0x368000A, 0x372000A, 0x37C000A, 0x386000A, 0x3900009, 0x399000A, + 0x3A30009, 0x3AD0009, 0x3B60009, 0x3BF000A, 0x3C90009, 0x3D20009, + 0x3DB0009, 0x3E40009, 0x3ED0009, 0x3F60009 }, + + /* 1 --> gamma 1/2 */ + { 0x7F, 0x800034, 0xB50028, 0xDE0021, 0x100001E, 0x11E001B, + 0x1390019, 0x1520017, 0x16A0015, 0x1800014, 0x1940014, 0x1A80013, + 0x1BB0012, 0x1CD0011, 0x1DF0010, 0x1EF0010, 0x200000F, 0x20F000F, + 0x21F000E, 0x22D000F, 0x23C000E, 0x24A000E, 0x258000D, 0x265000D, + 0x273000C, 0x27F000D, 0x28C000C, 0x299000C, 0x2A5000C, 0x2B1000B, + 0x2BC000C, 0x2C8000B, 0x2D3000C, 0x2DF000B, 0x2EA000A, 0x2F5000A, + 0x2FF000B, 0x30A000A, 0x314000B, 0x31F000A, 0x329000A, 0x333000A, + 0x33D0009, 0x3470009, 0x350000A, 0x35A0009, 0x363000A, 0x36D0009, + 0x3760009, 0x37F0009, 0x3880009, 0x3910009, 0x39A0009, 0x3A30009, + 0x3AC0008, 0x3B40009, 0x3BD0008, 0x3C60008, 0x3CE0008, 0x3D60009, + 0x3DF0008, 0x3E70008, 0x3EF0008, 0x3F70008 }, + + /* 2 --> gamma 1/2.2 */ + { 0x99, 0x9B0038, 0xD4002A, 0xFF0023, 0x122001F, 0x141001B, + 0x15D0019, 0x1760017, 0x18E0015, 0x1A30015, 0x1B80013, 0x1CC0012, + 0x1DE0011, 0x1F00010, 0x2010010, 0x2110010, 0x221000F, 0x230000F, + 0x23F000E, 0x24D000E, 0x25B000D, 0x269000C, 0x276000C, 0x283000C, + 0x28F000C, 0x29B000C, 0x2A7000C, 0x2B3000B, 0x2BF000B, 0x2CA000B, + 0x2D5000B, 0x2E0000A, 0x2EB000A, 0x2F5000A, 0x2FF000A, 0x30A000A, + 0x3140009, 0x31E0009, 0x327000A, 0x3310009, 0x33A0009, 0x3440009, + 0x34D0009, 0x3560009, 0x35F0009, 0x3680008, 0x3710008, 0x3790009, + 0x3820008, 0x38A0008, 0x3930008, 0x39B0008, 0x3A30008, 0x3AB0008, + 0x3B30008, 0x3BB0008, 0x3C30008, 0x3CB0007, 0x3D20008, 0x3DA0007, + 0x3E20007, 0x3E90007, 0x3F00008, 0x3F80007 }, +}; + +static int isc_parse_dt(struct device *dev, struct isc_device *isc) +{ + struct device_node *np = dev->of_node; + struct device_node *epn = NULL; + struct isc_subdev_entity *subdev_entity; + unsigned int flags; + int ret; + + INIT_LIST_HEAD(&isc->subdev_entities); + + while (1) { + struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 }; + + epn = of_graph_get_next_endpoint(np, epn); + if (!epn) + return 0; + + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn), + &v4l2_epn); + if (ret) { + ret = -EINVAL; + dev_err(dev, "Could not parse the endpoint\n"); + break; + } + + subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity), + GFP_KERNEL); + if (!subdev_entity) { + ret = -ENOMEM; + break; + } + subdev_entity->epn = epn; + + flags = v4l2_epn.bus.parallel.flags; + + if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) + subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW; + + if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) + subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW; + + if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) + subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW; + + if (v4l2_epn.bus_type == V4L2_MBUS_BT656) + subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC | + ISC_PFE_CFG0_CCIR656; + + list_add_tail(&subdev_entity->list, &isc->subdev_entities); + } + of_node_put(epn); + + return ret; +} + +static int atmel_isc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct isc_device *isc; + struct resource *res; + void __iomem *io_base; + struct isc_subdev_entity *subdev_entity; + int irq; + int ret; + u32 ver; + + isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL); + if (!isc) + return -ENOMEM; + + platform_set_drvdata(pdev, isc); + isc->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + io_base = devm_ioremap_resource(dev, res); + if (IS_ERR(io_base)) + return PTR_ERR(io_base); + + isc->regmap = devm_regmap_init_mmio(dev, io_base, &atmel_isc_regmap_config); + if (IS_ERR(isc->regmap)) { + ret = PTR_ERR(isc->regmap); + dev_err(dev, "failed to init register map: %d\n", ret); + return ret; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(dev, irq, atmel_isc_interrupt, 0, + "atmel-sama5d2-isc", isc); + if (ret < 0) { + dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n", + irq, ret); + return ret; + } + + isc->gamma_table = isc_sama5d2_gamma_table; + isc->gamma_max = 2; + + isc->max_width = ISC_SAMA5D2_MAX_SUPPORT_WIDTH; + isc->max_height = ISC_SAMA5D2_MAX_SUPPORT_HEIGHT; + + isc->config_dpc = isc_sama5d2_config_dpc; + isc->config_csc = isc_sama5d2_config_csc; + isc->config_cbc = isc_sama5d2_config_cbc; + isc->config_cc = isc_sama5d2_config_cc; + isc->config_gam = isc_sama5d2_config_gam; + isc->config_rlp = isc_sama5d2_config_rlp; + isc->config_ctrls = isc_sama5d2_config_ctrls; + + isc->adapt_pipeline = isc_sama5d2_adapt_pipeline; + + isc->offsets.csc = ISC_SAMA5D2_CSC_OFFSET; + isc->offsets.cbc = ISC_SAMA5D2_CBC_OFFSET; + isc->offsets.sub422 = ISC_SAMA5D2_SUB422_OFFSET; + isc->offsets.sub420 = ISC_SAMA5D2_SUB420_OFFSET; + isc->offsets.rlp = ISC_SAMA5D2_RLP_OFFSET; + isc->offsets.his = ISC_SAMA5D2_HIS_OFFSET; + isc->offsets.dma = ISC_SAMA5D2_DMA_OFFSET; + isc->offsets.version = ISC_SAMA5D2_VERSION_OFFSET; + isc->offsets.his_entry = ISC_SAMA5D2_HIS_ENTRY_OFFSET; + + isc->controller_formats = sama5d2_controller_formats; + isc->controller_formats_size = ARRAY_SIZE(sama5d2_controller_formats); + isc->formats_list = sama5d2_formats_list; + isc->formats_list_size = ARRAY_SIZE(sama5d2_formats_list); + + /* sama5d2-isc - 8 bits per beat */ + isc->dcfg = ISC_DCFG_YMBSIZE_BEATS8 | ISC_DCFG_CMBSIZE_BEATS8; + + /* sama5d2-isc : ISPCK is required and mandatory */ + isc->ispck_required = true; + + ret = atmel_isc_pipeline_init(isc); + if (ret) + return ret; + + isc->hclock = devm_clk_get(dev, "hclock"); + if (IS_ERR(isc->hclock)) { + ret = PTR_ERR(isc->hclock); + dev_err(dev, "failed to get hclock: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(isc->hclock); + if (ret) { + dev_err(dev, "failed to enable hclock: %d\n", ret); + return ret; + } + + ret = atmel_isc_clk_init(isc); + if (ret) { + dev_err(dev, "failed to init isc clock: %d\n", ret); + goto unprepare_hclk; + } + ret = v4l2_device_register(dev, &isc->v4l2_dev); + if (ret) { + dev_err(dev, "unable to register v4l2 device.\n"); + goto unprepare_clk; + } + + ret = isc_parse_dt(dev, isc); + if (ret) { + dev_err(dev, "fail to parse device tree\n"); + goto unregister_v4l2_device; + } + + if (list_empty(&isc->subdev_entities)) { + dev_err(dev, "no subdev found\n"); + ret = -ENODEV; + goto unregister_v4l2_device; + } + + list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { + struct v4l2_async_subdev *asd; + struct fwnode_handle *fwnode = + of_fwnode_handle(subdev_entity->epn); + + v4l2_async_nf_init(&subdev_entity->notifier); + + asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier, + fwnode, + struct v4l2_async_subdev); + + of_node_put(subdev_entity->epn); + subdev_entity->epn = NULL; + + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); + goto cleanup_subdev; + } + + subdev_entity->notifier.ops = &atmel_isc_async_ops; + + ret = v4l2_async_nf_register(&isc->v4l2_dev, + &subdev_entity->notifier); + if (ret) { + dev_err(dev, "fail to register async notifier\n"); + goto cleanup_subdev; + } + + if (video_is_registered(&isc->video_dev)) + break; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_request_idle(dev); + + isc->ispck = isc->isc_clks[ISC_ISPCK].clk; + + ret = clk_prepare_enable(isc->ispck); + if (ret) { + dev_err(dev, "failed to enable ispck: %d\n", ret); + goto disable_pm; + } + + /* ispck should be greater or equal to hclock */ + ret = clk_set_rate(isc->ispck, clk_get_rate(isc->hclock)); + if (ret) { + dev_err(dev, "failed to set ispck rate: %d\n", ret); + goto unprepare_clk; + } + + regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver); + dev_info(dev, "Microchip ISC version %x\n", ver); + + return 0; + +unprepare_clk: + clk_disable_unprepare(isc->ispck); + +disable_pm: + pm_runtime_disable(dev); + +cleanup_subdev: + atmel_isc_subdev_cleanup(isc); + +unregister_v4l2_device: + v4l2_device_unregister(&isc->v4l2_dev); + +unprepare_hclk: + clk_disable_unprepare(isc->hclock); + + atmel_isc_clk_cleanup(isc); + + return ret; +} + +static int atmel_isc_remove(struct platform_device *pdev) +{ + struct isc_device *isc = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + + atmel_isc_subdev_cleanup(isc); + + v4l2_device_unregister(&isc->v4l2_dev); + + clk_disable_unprepare(isc->ispck); + clk_disable_unprepare(isc->hclock); + + atmel_isc_clk_cleanup(isc); + + return 0; +} + +static int __maybe_unused isc_runtime_suspend(struct device *dev) +{ + struct isc_device *isc = dev_get_drvdata(dev); + + clk_disable_unprepare(isc->ispck); + clk_disable_unprepare(isc->hclock); + + return 0; +} + +static int __maybe_unused isc_runtime_resume(struct device *dev) +{ + struct isc_device *isc = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(isc->hclock); + if (ret) + return ret; + + ret = clk_prepare_enable(isc->ispck); + if (ret) + clk_disable_unprepare(isc->hclock); + + return ret; +} + +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, + .remove = atmel_isc_remove, + .driver = { + .name = "atmel-sama5d2-isc", + .pm = &atmel_isc_dev_pm_ops, + .of_match_table = of_match_ptr(atmel_isc_of_match), + }, +}; + +module_platform_driver(atmel_isc_driver); + +MODULE_AUTHOR("Songjun Wu"); +MODULE_DESCRIPTION("The V4L2 driver for Atmel-ISC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c b/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c new file mode 100644 index 000000000000..01ababdfcbd9 --- /dev/null +++ b/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c @@ -0,0 +1,616 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip eXtended Image Sensor Controller (XISC) driver + * + * Copyright (C) 2019-2021 Microchip Technology, Inc. and its subsidiaries + * + * Author: Eugen Hristev <eugen.hristev@microchip.com> + * + * Sensor-->PFE-->DPC-->WB-->CFA-->CC-->GAM-->VHXS-->CSC-->CBHS-->SUB-->RLP-->DMA-->HIS + * + * ISC video pipeline integrates the following submodules: + * PFE: Parallel Front End to sample the camera sensor input stream + * DPC: Defective Pixel Correction with black offset correction, green disparity + * correction and defective pixel correction (3 modules total) + * WB: Programmable white balance in the Bayer domain + * CFA: Color filter array interpolation module + * CC: Programmable color correction + * GAM: Gamma correction + *VHXS: Vertical and Horizontal Scaler + * CSC: Programmable color space conversion + *CBHS: Contrast Brightness Hue and Saturation control + * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling + * RLP: This module performs rounding, range limiting + * and packing of the incoming data + * DMA: This module performs DMA master accesses to write frames to external RAM + * HIS: Histogram module performs statistic counters on the frames + */ + +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/math64.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/videodev2.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-image-sizes.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> +#include <media/videobuf2-dma-contig.h> + +#include "atmel-isc-regs.h" +#include "atmel-isc.h" + +#define ISC_SAMA7G5_MAX_SUPPORT_WIDTH 3264 +#define ISC_SAMA7G5_MAX_SUPPORT_HEIGHT 2464 + +#define ISC_SAMA7G5_PIPELINE \ + (WB_ENABLE | CFA_ENABLE | CC_ENABLE | GAM_ENABLES | CSC_ENABLE | \ + CBC_ENABLE | SUB422_ENABLE | SUB420_ENABLE) + +/* This is a list of the formats that the ISC can *output* */ +static const struct isc_format sama7g5_controller_formats[] = { + { + .fourcc = V4L2_PIX_FMT_ARGB444, + }, { + .fourcc = V4L2_PIX_FMT_ARGB555, + }, { + .fourcc = V4L2_PIX_FMT_RGB565, + }, { + .fourcc = V4L2_PIX_FMT_ABGR32, + }, { + .fourcc = V4L2_PIX_FMT_XBGR32, + }, { + .fourcc = V4L2_PIX_FMT_YUV420, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + }, { + .fourcc = V4L2_PIX_FMT_VYUY, + }, { + .fourcc = V4L2_PIX_FMT_YUYV, + }, { + .fourcc = V4L2_PIX_FMT_YUV422P, + }, { + .fourcc = V4L2_PIX_FMT_GREY, + }, { + .fourcc = V4L2_PIX_FMT_Y10, + }, { + .fourcc = V4L2_PIX_FMT_Y16, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR8, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG8, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG8, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB8, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR10, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG10, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG10, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB10, + }, +}; + +/* This is a list of formats that the ISC can receive as *input* */ +static struct isc_format sama7g5_formats_list[] = { + { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, + .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, + .cfa_baycfg = ISC_BAY_CFG_BGBG, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, + .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, + .cfa_baycfg = ISC_BAY_CFG_GBGB, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, + .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, + .cfa_baycfg = ISC_BAY_CFG_GRGR, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, + .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, + .cfa_baycfg = ISC_BAY_CFG_RGRG, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, + .cfa_baycfg = ISC_BAY_CFG_RGRG, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, + .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, + .cfa_baycfg = ISC_BAY_CFG_GBGB, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, + .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, + .cfa_baycfg = ISC_BAY_CFG_GRGR, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, + .cfa_baycfg = ISC_BAY_CFG_RGRG, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, + .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, + .cfa_baycfg = ISC_BAY_CFG_BGBG, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, + .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, + .cfa_baycfg = ISC_BAY_CFG_GBGB, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, + .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, + .cfa_baycfg = ISC_BAY_CFG_GRGR, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB12, + .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, + .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, + .cfa_baycfg = ISC_BAY_CFG_RGRG, + }, + { + .fourcc = V4L2_PIX_FMT_GREY, + .mbus_code = MEDIA_BUS_FMT_Y8_1X8, + .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, + }, + { + .fourcc = V4L2_PIX_FMT_YUYV, + .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, + .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, + }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, + .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, + }, + { + .fourcc = V4L2_PIX_FMT_RGB565, + .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, + }, + { + .fourcc = V4L2_PIX_FMT_Y10, + .mbus_code = MEDIA_BUS_FMT_Y10_1X10, + .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, + }, +}; + +static void isc_sama7g5_config_csc(struct isc_device *isc) +{ + struct regmap *regmap = isc->regmap; + + /* Convert RGB to YUV */ + regmap_write(regmap, ISC_CSC_YR_YG + isc->offsets.csc, + 0x42 | (0x81 << 16)); + regmap_write(regmap, ISC_CSC_YB_OY + isc->offsets.csc, + 0x19 | (0x10 << 16)); + regmap_write(regmap, ISC_CSC_CBR_CBG + isc->offsets.csc, + 0xFDA | (0xFB6 << 16)); + regmap_write(regmap, ISC_CSC_CBB_OCB + isc->offsets.csc, + 0x70 | (0x80 << 16)); + regmap_write(regmap, ISC_CSC_CRR_CRG + isc->offsets.csc, + 0x70 | (0xFA2 << 16)); + regmap_write(regmap, ISC_CSC_CRB_OCR + isc->offsets.csc, + 0xFEE | (0x80 << 16)); +} + +static void isc_sama7g5_config_cbc(struct isc_device *isc) +{ + struct regmap *regmap = isc->regmap; + + /* Configure what is set via v4l2 ctrls */ + regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc, isc->ctrls.brightness); + regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc, isc->ctrls.contrast); + /* Configure Hue and Saturation as neutral midpoint */ + regmap_write(regmap, ISC_CBCHS_HUE, 0); + regmap_write(regmap, ISC_CBCHS_SAT, (1 << 4)); +} + +static void isc_sama7g5_config_cc(struct isc_device *isc) +{ + struct regmap *regmap = isc->regmap; + + /* Configure each register at the neutral fixed point 1.0 or 0.0 */ + regmap_write(regmap, ISC_CC_RR_RG, (1 << 8)); + regmap_write(regmap, ISC_CC_RB_OR, 0); + regmap_write(regmap, ISC_CC_GR_GG, (1 << 8) << 16); + regmap_write(regmap, ISC_CC_GB_OG, 0); + regmap_write(regmap, ISC_CC_BR_BG, 0); + regmap_write(regmap, ISC_CC_BB_OB, (1 << 8)); +} + +static void isc_sama7g5_config_ctrls(struct isc_device *isc, + const struct v4l2_ctrl_ops *ops) +{ + struct isc_ctrls *ctrls = &isc->ctrls; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; + + ctrls->contrast = 16; + + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 16); +} + +static void isc_sama7g5_config_dpc(struct isc_device *isc) +{ + u32 bay_cfg = isc->config.sd_format->cfa_baycfg; + struct regmap *regmap = isc->regmap; + + regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BLOFF_MASK, + (64 << ISC_DPC_CFG_BLOFF_SHIFT)); + regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BAYCFG_MASK, + (bay_cfg << ISC_DPC_CFG_BAYCFG_SHIFT)); +} + +static void isc_sama7g5_config_gam(struct isc_device *isc) +{ + struct regmap *regmap = isc->regmap; + + regmap_update_bits(regmap, ISC_GAM_CTRL, ISC_GAM_CTRL_BIPART, + ISC_GAM_CTRL_BIPART); +} + +static void isc_sama7g5_config_rlp(struct isc_device *isc) +{ + struct regmap *regmap = isc->regmap; + u32 rlp_mode = isc->config.rlp_cfg_mode; + + regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp, + ISC_RLP_CFG_MODE_MASK | ISC_RLP_CFG_LSH | + ISC_RLP_CFG_YMODE_MASK, rlp_mode); +} + +static void isc_sama7g5_adapt_pipeline(struct isc_device *isc) +{ + isc->try_config.bits_pipeline &= ISC_SAMA7G5_PIPELINE; +} + +/* Gamma table with gamma 1/2.2 */ +static const u32 isc_sama7g5_gamma_table[][GAMMA_ENTRIES] = { + /* index 0 --> gamma bipartite */ + { + 0x980, 0x4c0320, 0x650260, 0x7801e0, 0x8701a0, 0x940180, + 0xa00160, 0xab0120, 0xb40120, 0xbd0120, 0xc60100, 0xce0100, + 0xd600e0, 0xdd00e0, 0xe400e0, 0xeb00c0, 0xf100c0, 0xf700c0, + 0xfd00c0, 0x10300a0, 0x10800c0, 0x10e00a0, 0x11300a0, 0x11800a0, + 0x11d00a0, 0x12200a0, 0x12700a0, 0x12c0080, 0x13000a0, 0x1350080, + 0x13900a0, 0x13e0080, 0x1420076, 0x17d0062, 0x1ae0054, 0x1d8004a, + 0x1fd0044, 0x21f003e, 0x23e003a, 0x25b0036, 0x2760032, 0x28f0030, + 0x2a7002e, 0x2be002c, 0x2d4002c, 0x2ea0028, 0x2fe0028, 0x3120026, + 0x3250024, 0x3370024, 0x3490022, 0x35a0022, 0x36b0020, 0x37b0020, + 0x38b0020, 0x39b001e, 0x3aa001e, 0x3b9001c, 0x3c7001c, 0x3d5001c, + 0x3e3001c, 0x3f1001c, 0x3ff001a, 0x40c001a }, +}; + +static int xisc_parse_dt(struct device *dev, struct isc_device *isc) +{ + struct device_node *np = dev->of_node; + struct device_node *epn = NULL; + struct isc_subdev_entity *subdev_entity; + unsigned int flags; + int ret; + bool mipi_mode; + + INIT_LIST_HEAD(&isc->subdev_entities); + + mipi_mode = of_property_read_bool(np, "microchip,mipi-mode"); + + while (1) { + struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 }; + + epn = of_graph_get_next_endpoint(np, epn); + if (!epn) + return 0; + + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn), + &v4l2_epn); + if (ret) { + ret = -EINVAL; + dev_err(dev, "Could not parse the endpoint\n"); + break; + } + + subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity), + GFP_KERNEL); + if (!subdev_entity) { + ret = -ENOMEM; + break; + } + subdev_entity->epn = epn; + + flags = v4l2_epn.bus.parallel.flags; + + if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) + subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW; + + if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) + subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW; + + if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) + subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW; + + if (v4l2_epn.bus_type == V4L2_MBUS_BT656) + subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC | + ISC_PFE_CFG0_CCIR656; + + if (mipi_mode) + subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_MIPI; + + list_add_tail(&subdev_entity->list, &isc->subdev_entities); + } + of_node_put(epn); + + return ret; +} + +static int microchip_xisc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct isc_device *isc; + struct resource *res; + void __iomem *io_base; + struct isc_subdev_entity *subdev_entity; + int irq; + int ret; + u32 ver; + + isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL); + if (!isc) + return -ENOMEM; + + platform_set_drvdata(pdev, isc); + isc->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + io_base = devm_ioremap_resource(dev, res); + if (IS_ERR(io_base)) + return PTR_ERR(io_base); + + isc->regmap = devm_regmap_init_mmio(dev, io_base, &atmel_isc_regmap_config); + if (IS_ERR(isc->regmap)) { + ret = PTR_ERR(isc->regmap); + dev_err(dev, "failed to init register map: %d\n", ret); + return ret; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(dev, irq, atmel_isc_interrupt, 0, + "microchip-sama7g5-xisc", isc); + if (ret < 0) { + dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n", + irq, ret); + return ret; + } + + isc->gamma_table = isc_sama7g5_gamma_table; + isc->gamma_max = 0; + + isc->max_width = ISC_SAMA7G5_MAX_SUPPORT_WIDTH; + isc->max_height = ISC_SAMA7G5_MAX_SUPPORT_HEIGHT; + + isc->config_dpc = isc_sama7g5_config_dpc; + isc->config_csc = isc_sama7g5_config_csc; + isc->config_cbc = isc_sama7g5_config_cbc; + isc->config_cc = isc_sama7g5_config_cc; + isc->config_gam = isc_sama7g5_config_gam; + isc->config_rlp = isc_sama7g5_config_rlp; + isc->config_ctrls = isc_sama7g5_config_ctrls; + + isc->adapt_pipeline = isc_sama7g5_adapt_pipeline; + + isc->offsets.csc = ISC_SAMA7G5_CSC_OFFSET; + isc->offsets.cbc = ISC_SAMA7G5_CBC_OFFSET; + isc->offsets.sub422 = ISC_SAMA7G5_SUB422_OFFSET; + isc->offsets.sub420 = ISC_SAMA7G5_SUB420_OFFSET; + isc->offsets.rlp = ISC_SAMA7G5_RLP_OFFSET; + isc->offsets.his = ISC_SAMA7G5_HIS_OFFSET; + isc->offsets.dma = ISC_SAMA7G5_DMA_OFFSET; + isc->offsets.version = ISC_SAMA7G5_VERSION_OFFSET; + isc->offsets.his_entry = ISC_SAMA7G5_HIS_ENTRY_OFFSET; + + isc->controller_formats = sama7g5_controller_formats; + isc->controller_formats_size = ARRAY_SIZE(sama7g5_controller_formats); + isc->formats_list = sama7g5_formats_list; + isc->formats_list_size = ARRAY_SIZE(sama7g5_formats_list); + + /* sama7g5-isc RAM access port is full AXI4 - 32 bits per beat */ + isc->dcfg = ISC_DCFG_YMBSIZE_BEATS32 | ISC_DCFG_CMBSIZE_BEATS32; + + /* sama7g5-isc : ISPCK does not exist, ISC is clocked by MCK */ + isc->ispck_required = false; + + ret = atmel_isc_pipeline_init(isc); + if (ret) + return ret; + + isc->hclock = devm_clk_get(dev, "hclock"); + if (IS_ERR(isc->hclock)) { + ret = PTR_ERR(isc->hclock); + dev_err(dev, "failed to get hclock: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(isc->hclock); + if (ret) { + dev_err(dev, "failed to enable hclock: %d\n", ret); + return ret; + } + + ret = atmel_isc_clk_init(isc); + if (ret) { + dev_err(dev, "failed to init isc clock: %d\n", ret); + goto unprepare_hclk; + } + + ret = v4l2_device_register(dev, &isc->v4l2_dev); + if (ret) { + dev_err(dev, "unable to register v4l2 device.\n"); + goto unprepare_hclk; + } + + ret = xisc_parse_dt(dev, isc); + if (ret) { + dev_err(dev, "fail to parse device tree\n"); + goto unregister_v4l2_device; + } + + if (list_empty(&isc->subdev_entities)) { + dev_err(dev, "no subdev found\n"); + ret = -ENODEV; + goto unregister_v4l2_device; + } + + list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { + struct v4l2_async_subdev *asd; + struct fwnode_handle *fwnode = + of_fwnode_handle(subdev_entity->epn); + + v4l2_async_nf_init(&subdev_entity->notifier); + + asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier, + fwnode, + struct v4l2_async_subdev); + + of_node_put(subdev_entity->epn); + subdev_entity->epn = NULL; + + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); + goto cleanup_subdev; + } + + subdev_entity->notifier.ops = &atmel_isc_async_ops; + + ret = v4l2_async_nf_register(&isc->v4l2_dev, + &subdev_entity->notifier); + if (ret) { + dev_err(dev, "fail to register async notifier\n"); + goto cleanup_subdev; + } + + if (video_is_registered(&isc->video_dev)) + break; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_request_idle(dev); + + regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver); + dev_info(dev, "Microchip XISC version %x\n", ver); + + return 0; + +cleanup_subdev: + atmel_isc_subdev_cleanup(isc); + +unregister_v4l2_device: + v4l2_device_unregister(&isc->v4l2_dev); + +unprepare_hclk: + clk_disable_unprepare(isc->hclock); + + atmel_isc_clk_cleanup(isc); + + return ret; +} + +static int microchip_xisc_remove(struct platform_device *pdev) +{ + struct isc_device *isc = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + + atmel_isc_subdev_cleanup(isc); + + v4l2_device_unregister(&isc->v4l2_dev); + + clk_disable_unprepare(isc->hclock); + + atmel_isc_clk_cleanup(isc); + + return 0; +} + +static int __maybe_unused xisc_runtime_suspend(struct device *dev) +{ + struct isc_device *isc = dev_get_drvdata(dev); + + clk_disable_unprepare(isc->hclock); + + return 0; +} + +static int __maybe_unused xisc_runtime_resume(struct device *dev) +{ + struct isc_device *isc = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(isc->hclock); + if (ret) + return ret; + + return ret; +} + +static const struct dev_pm_ops microchip_xisc_dev_pm_ops = { + SET_RUNTIME_PM_OPS(xisc_runtime_suspend, xisc_runtime_resume, NULL) +}; + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id microchip_xisc_of_match[] = { + { .compatible = "microchip,sama7g5-isc" }, + { } +}; +MODULE_DEVICE_TABLE(of, microchip_xisc_of_match); +#endif + +static struct platform_driver microchip_xisc_driver = { + .probe = microchip_xisc_probe, + .remove = microchip_xisc_remove, + .driver = { + .name = "microchip-sama7g5-xisc", + .pm = µchip_xisc_dev_pm_ops, + .of_match_table = of_match_ptr(microchip_xisc_of_match), + }, +}; + +module_platform_driver(microchip_xisc_driver); + +MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>"); +MODULE_DESCRIPTION("The V4L2 driver for Microchip-XISC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/deprecated/stkwebcam/Kconfig b/drivers/staging/media/deprecated/stkwebcam/Kconfig index 4450403dff41..7234498e634a 100644 --- a/drivers/staging/media/deprecated/stkwebcam/Kconfig +++ b/drivers/staging/media/deprecated/stkwebcam/Kconfig @@ -2,7 +2,7 @@ config VIDEO_STKWEBCAM tristate "USB Syntek DC1125 Camera support (DEPRECATED)" depends on VIDEO_DEV - depends on USB + depends on MEDIA_USB_SUPPORT && MEDIA_CAMERA_SUPPORT help Say Y here if you want to use this type of camera. Supported devices are typically found in some Asus laptops, diff --git a/drivers/staging/media/imx/Kconfig b/drivers/staging/media/imx/Kconfig index 0bacac302d7e..21fd79515042 100644 --- a/drivers/staging/media/imx/Kconfig +++ b/drivers/staging/media/imx/Kconfig @@ -23,12 +23,15 @@ config VIDEO_IMX_CSI default y help A video4linux camera sensor interface driver for i.MX5/6. - -config VIDEO_IMX7_CSI - tristate "i.MX6UL/L / i.MX7 / i.MX8M Camera Sensor Interface driver" - default y - help - Enable support for video4linux camera sensor interface driver for - i.MX6UL/L, i.MX7 or i.MX8M. endmenu endif + +config VIDEO_IMX8MQ_MIPI_CSI2 + tristate "NXP i.MX8MQ MIPI CSI-2 receiver" + depends on ARCH_MXC || COMPILE_TEST + depends on VIDEO_DEV + select MEDIA_CONTROLLER + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + help + V4L2 driver for the MIPI CSI-2 receiver found in the i.MX8MQ SoC. diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile index d82be898145b..906a422aa656 100644 --- a/drivers/staging/media/imx/Makefile +++ b/drivers/staging/media/imx/Makefile @@ -14,5 +14,4 @@ obj-$(CONFIG_VIDEO_IMX_CSI) += imx6-media.o obj-$(CONFIG_VIDEO_IMX_CSI) += imx6-media-csi.o obj-$(CONFIG_VIDEO_IMX_CSI) += imx6-mipi-csi2.o -obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-media-csi.o -obj-$(CONFIG_VIDEO_IMX7_CSI) += imx8mq-mipi-csi2.o +obj-$(CONFIG_VIDEO_IMX8MQ_MIPI_CSI2) += imx8mq-mipi-csi2.o diff --git a/drivers/staging/media/imx/TODO b/drivers/staging/media/imx/TODO index 5d3a337c8702..11c9e10d34ae 100644 --- a/drivers/staging/media/imx/TODO +++ b/drivers/staging/media/imx/TODO @@ -2,18 +2,6 @@ - The Frame Interval Monitor could be exported to v4l2-core for general use. -- The CSI subdevice parses its nearest upstream neighbor's device-tree - bus config in order to setup the CSI. Laurent Pinchart argues that - instead the CSI subdev should call its neighbor's g_mbus_config op - (which should be propagated if necessary) to get this info. However - Hans Verkuil is planning to remove the g_mbus_config op. For now this - driver uses the parsed DT bus config method until this issue is - resolved. - - 2020-06: g_mbus has been removed in favour of the get_mbus_config pad - operation which should be used to avoid parsing the remote endpoint - configuration. - - This media driver supports inheriting V4L2 controls to the video capture devices, from the subdevices in the capture device's pipeline. The controls for each capture device are updated in the @@ -23,32 +11,3 @@ - Similarly to the legacy control handling, legacy format handling where formats on the video nodes are influenced by the active format of the connected subdev should be removed. - -- i.MX7: all of the above, since it uses the imx media core - -- i.MX7: use Frame Interval Monitor - -- imx7-media-csi: Restrict the supported formats list to the SoC version. - - The imx7 CSI bridge can be configured to sample pixel components from the Rx - queue in single (8bpp) or double (16bpp) component modes. Image format - variants with different sample sizes (ie YUYV_2X8 vs YUYV_1X16) determine the - pixel components sampling size per each clock cycle and their packing mode - (see imx7_csi_configure() for details). - - As the imx7 CSI bridge can be interfaced with different IP blocks depending on - the SoC model it is integrated on, the Rx queue sampling size should match - the size of the samples transferred by the transmitting IP block. - - To avoid mis-configurations of the capture pipeline, the enumeration of the - supported formats should be restricted to match the pixel source transmitting - mode. - - Example: i.MX8MM SoC integrates the CSI bridge with the Samsung CSIS CSI-2 - receiver which operates in dual pixel sampling mode. The CSI bridge should - only expose the 1X16 formats variant which instructs it to operate in dual - pixel sampling mode. When the CSI bridge is instead integrated on an i.MX7, - which supports both serial and parallel input, it should expose both variants. - - This currently only applies to YUYV formats, but other formats might need - to be handled in the same way. diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index b2b1f4dd41d7..5c3cc7de209d 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -97,8 +97,8 @@ struct csi_priv { /* the mipi virtual channel number at link validate */ int vc_num; - /* the upstream endpoint CSI is receiving from */ - struct v4l2_fwnode_endpoint upstream_ep; + /* media bus config of the upstream subdevice CSI is receiving from */ + struct v4l2_mbus_config mbus_cfg; spinlock_t irqlock; /* protect eof_irq handler */ struct timer_list eof_timeout_timer; @@ -125,14 +125,14 @@ static inline struct csi_priv *notifier_to_dev(struct v4l2_async_notifier *n) return container_of(n, struct csi_priv, notifier); } -static inline bool is_parallel_bus(struct v4l2_fwnode_endpoint *ep) +static inline bool is_parallel_bus(struct v4l2_mbus_config *mbus_cfg) { - return ep->bus_type != V4L2_MBUS_CSI2_DPHY; + return mbus_cfg->type != V4L2_MBUS_CSI2_DPHY; } -static inline bool is_parallel_16bit_bus(struct v4l2_fwnode_endpoint *ep) +static inline bool is_parallel_16bit_bus(struct v4l2_mbus_config *mbus_cfg) { - return is_parallel_bus(ep) && ep->bus.parallel.bus_width >= 16; + return is_parallel_bus(mbus_cfg) && mbus_cfg->bus.parallel.bus_width >= 16; } /* @@ -145,36 +145,31 @@ static inline bool is_parallel_16bit_bus(struct v4l2_fwnode_endpoint *ep) * - the CSI is receiving from an 8-bit parallel bus and the incoming * media bus format is other than UYVY8_2X8/YUYV8_2X8. */ -static inline bool requires_passthrough(struct v4l2_fwnode_endpoint *ep, +static inline bool requires_passthrough(struct v4l2_mbus_config *mbus_cfg, struct v4l2_mbus_framefmt *infmt, const struct imx_media_pixfmt *incc) { - if (ep->bus_type == V4L2_MBUS_BT656) // including BT.1120 + if (mbus_cfg->type == V4L2_MBUS_BT656) // including BT.1120 return false; - return incc->bayer || is_parallel_16bit_bus(ep) || - (is_parallel_bus(ep) && + return incc->bayer || is_parallel_16bit_bus(mbus_cfg) || + (is_parallel_bus(mbus_cfg) && infmt->code != MEDIA_BUS_FMT_UYVY8_2X8 && infmt->code != MEDIA_BUS_FMT_YUYV8_2X8); } /* - * Parses the fwnode endpoint from the source pad of the entity - * connected to this CSI. This will either be the entity directly - * upstream from the CSI-2 receiver, directly upstream from the - * video mux, or directly upstream from the CSI itself. The endpoint - * is needed to determine the bus type and bus config coming into - * the CSI. + * Queries the media bus config of the upstream entity that provides data to + * the CSI. This will either be the entity directly upstream from the CSI-2 + * receiver, directly upstream from a video mux, or directly upstream from + * the CSI itself. */ -static int csi_get_upstream_endpoint(struct csi_priv *priv, - struct v4l2_fwnode_endpoint *ep) +static int csi_get_upstream_mbus_config(struct csi_priv *priv, + struct v4l2_mbus_config *mbus_cfg) { - struct fwnode_handle *endpoint; - struct v4l2_subdev *sd; - struct media_pad *pad; - - if (!IS_ENABLED(CONFIG_OF)) - return -ENXIO; + struct v4l2_subdev *sd, *remote_sd; + struct media_pad *remote_pad; + int ret; if (!priv->src_sd) return -EPIPE; @@ -206,19 +201,21 @@ static int csi_get_upstream_endpoint(struct csi_priv *priv, } /* get source pad of entity directly upstream from sd */ - pad = imx_media_pipeline_pad(&sd->entity, 0, 0, true); - if (!pad) - return -ENODEV; - - endpoint = imx_media_get_pad_fwnode(pad); - if (IS_ERR(endpoint)) - return PTR_ERR(endpoint); + remote_pad = media_entity_remote_pad_unique(&sd->entity, + MEDIA_PAD_FL_SOURCE); + if (IS_ERR(remote_pad)) + return PTR_ERR(remote_pad); - v4l2_fwnode_endpoint_parse(endpoint, ep); + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); - fwnode_handle_put(endpoint); + ret = v4l2_subdev_call(remote_sd, pad, get_mbus_config, + remote_pad->index, mbus_cfg); + if (ret == -ENOIOCTLCMD) + v4l2_err(&priv->sd, + "entity %s does not implement get_mbus_config()\n", + remote_pad->entity->name); - return 0; + return ret; } static void csi_idmac_put_ipu_resources(struct csi_priv *priv) @@ -435,7 +432,7 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) image.phys0 = phys[0]; image.phys1 = phys[1]; - passthrough = requires_passthrough(&priv->upstream_ep, infmt, incc); + passthrough = requires_passthrough(&priv->mbus_cfg, infmt, incc); passthrough_cycles = 1; /* @@ -708,7 +705,6 @@ static int csi_setup(struct csi_priv *priv) { struct v4l2_mbus_framefmt *infmt, *outfmt; const struct imx_media_pixfmt *incc; - struct v4l2_mbus_config mbus_cfg; struct v4l2_mbus_framefmt if_fmt; struct v4l2_rect crop; @@ -716,13 +712,6 @@ static int csi_setup(struct csi_priv *priv) incc = priv->cc[CSI_SINK_PAD]; outfmt = &priv->format_mbus[priv->active_output_pad]; - /* compose mbus_config from the upstream endpoint */ - mbus_cfg.type = priv->upstream_ep.bus_type; - if (is_parallel_bus(&priv->upstream_ep)) - mbus_cfg.bus.parallel = priv->upstream_ep.bus.parallel; - else - mbus_cfg.bus.mipi_csi2 = priv->upstream_ep.bus.mipi_csi2; - if_fmt = *infmt; crop = priv->crop; @@ -730,7 +719,7 @@ static int csi_setup(struct csi_priv *priv) * if cycles is set, we need to handle this over multiple cycles as * generic/bayer data */ - if (is_parallel_bus(&priv->upstream_ep) && incc->cycles) { + if (is_parallel_bus(&priv->mbus_cfg) && incc->cycles) { if_fmt.width *= incc->cycles; crop.width *= incc->cycles; } @@ -741,7 +730,7 @@ static int csi_setup(struct csi_priv *priv) priv->crop.width == 2 * priv->compose.width, priv->crop.height == 2 * priv->compose.height); - ipu_csi_init_interface(priv->csi, &mbus_cfg, &if_fmt, outfmt); + ipu_csi_init_interface(priv->csi, &priv->mbus_cfg, &if_fmt, outfmt); ipu_csi_set_dest(priv->csi, priv->dest); @@ -769,7 +758,7 @@ static int csi_start(struct csi_priv *priv) return ret; /* Skip first few frames from a BT.656 source */ - if (priv->upstream_ep.bus_type == V4L2_MBUS_BT656) { + if (priv->mbus_cfg.type == V4L2_MBUS_BT656) { u32 delay_usec, bad_frames = 20; delay_usec = DIV_ROUND_UP_ULL((u64)USEC_PER_SEC * @@ -1118,7 +1107,7 @@ static int csi_link_validate(struct v4l2_subdev *sd, struct v4l2_subdev_format *sink_fmt) { struct csi_priv *priv = v4l2_get_subdevdata(sd); - struct v4l2_fwnode_endpoint upstream_ep = { .bus_type = 0 }; + struct v4l2_mbus_config mbus_cfg = { .type = 0 }; bool is_csi2; int ret; @@ -1127,16 +1116,17 @@ static int csi_link_validate(struct v4l2_subdev *sd, if (ret) return ret; - ret = csi_get_upstream_endpoint(priv, &upstream_ep); + ret = csi_get_upstream_mbus_config(priv, &mbus_cfg); if (ret) { - v4l2_err(&priv->sd, "failed to find upstream endpoint\n"); + v4l2_err(&priv->sd, + "failed to get upstream media bus configuration\n"); return ret; } mutex_lock(&priv->lock); - priv->upstream_ep = upstream_ep; - is_csi2 = !is_parallel_bus(&upstream_ep); + priv->mbus_cfg = mbus_cfg; + is_csi2 = !is_parallel_bus(&mbus_cfg); if (is_csi2) { /* * NOTE! It seems the virtual channels from the mipi csi-2 @@ -1192,7 +1182,7 @@ static void csi_try_crop(struct csi_priv *priv, struct v4l2_rect *crop, struct v4l2_subdev_state *sd_state, struct v4l2_mbus_framefmt *infmt, - struct v4l2_fwnode_endpoint *upstream_ep) + struct v4l2_mbus_config *mbus_cfg) { u32 in_height; @@ -1216,7 +1206,7 @@ static void csi_try_crop(struct csi_priv *priv, * sync, so fix it to NTSC/PAL active lines. NTSC contains * 2 extra lines of active video that need to be cropped. */ - if (upstream_ep->bus_type == V4L2_MBUS_BT656 && + if (mbus_cfg->type == V4L2_MBUS_BT656 && (V4L2_FIELD_HAS_BOTH(infmt->field) || infmt->field == V4L2_FIELD_ALTERNATE)) { crop->height = in_height; @@ -1233,7 +1223,7 @@ static int csi_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_mbus_code_enum *code) { struct csi_priv *priv = v4l2_get_subdevdata(sd); - struct v4l2_fwnode_endpoint upstream_ep = { .bus_type = 0 }; + struct v4l2_mbus_config mbus_cfg = { .type = 0 }; const struct imx_media_pixfmt *incc; struct v4l2_mbus_framefmt *infmt; int ret = 0; @@ -1250,13 +1240,14 @@ static int csi_enum_mbus_code(struct v4l2_subdev *sd, break; case CSI_SRC_PAD_DIRECT: case CSI_SRC_PAD_IDMAC: - ret = csi_get_upstream_endpoint(priv, &upstream_ep); + ret = csi_get_upstream_mbus_config(priv, &mbus_cfg); if (ret) { - v4l2_err(&priv->sd, "failed to find upstream endpoint\n"); + v4l2_err(&priv->sd, + "failed to get upstream media bus configuration\n"); goto out; } - if (requires_passthrough(&upstream_ep, infmt, incc)) { + if (requires_passthrough(&mbus_cfg, infmt, incc)) { if (code->index != 0) { ret = -EINVAL; goto out; @@ -1426,7 +1417,7 @@ static void csi_try_field(struct csi_priv *priv, } static void csi_try_fmt(struct csi_priv *priv, - struct v4l2_fwnode_endpoint *upstream_ep, + struct v4l2_mbus_config *mbus_cfg, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat, struct v4l2_rect *crop, @@ -1447,7 +1438,7 @@ static void csi_try_fmt(struct csi_priv *priv, sdformat->format.width = compose->width; sdformat->format.height = compose->height; - if (requires_passthrough(upstream_ep, infmt, incc)) { + if (requires_passthrough(mbus_cfg, infmt, incc)) { sdformat->format.code = infmt->code; *cc = incc; } else { @@ -1497,8 +1488,7 @@ static void csi_try_fmt(struct csi_priv *priv, crop->height = sdformat->format.height; if (sdformat->format.field == V4L2_FIELD_ALTERNATE) crop->height *= 2; - csi_try_crop(priv, crop, sd_state, &sdformat->format, - upstream_ep); + csi_try_crop(priv, crop, sd_state, &sdformat->format, mbus_cfg); compose->left = 0; compose->top = 0; compose->width = crop->width; @@ -1516,7 +1506,7 @@ static int csi_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *sdformat) { struct csi_priv *priv = v4l2_get_subdevdata(sd); - struct v4l2_fwnode_endpoint upstream_ep = { .bus_type = 0 }; + struct v4l2_mbus_config mbus_cfg = { .type = 0 }; const struct imx_media_pixfmt *cc; struct v4l2_mbus_framefmt *fmt; struct v4l2_rect *crop, *compose; @@ -1525,9 +1515,10 @@ static int csi_set_fmt(struct v4l2_subdev *sd, if (sdformat->pad >= CSI_NUM_PADS) return -EINVAL; - ret = csi_get_upstream_endpoint(priv, &upstream_ep); + ret = csi_get_upstream_mbus_config(priv, &mbus_cfg); if (ret) { - v4l2_err(&priv->sd, "failed to find upstream endpoint\n"); + v4l2_err(&priv->sd, + "failed to get upstream media bus configuration\n"); return ret; } @@ -1541,8 +1532,7 @@ static int csi_set_fmt(struct v4l2_subdev *sd, crop = __csi_get_crop(priv, sd_state, sdformat->which); compose = __csi_get_compose(priv, sd_state, sdformat->which); - csi_try_fmt(priv, &upstream_ep, sd_state, sdformat, crop, compose, - &cc); + csi_try_fmt(priv, &mbus_cfg, sd_state, sdformat, crop, compose, &cc); fmt = __csi_get_fmt(priv, sd_state, sdformat->pad, sdformat->which); *fmt = sdformat->format; @@ -1559,8 +1549,8 @@ static int csi_set_fmt(struct v4l2_subdev *sd, format.pad = pad; format.which = sdformat->which; format.format = sdformat->format; - csi_try_fmt(priv, &upstream_ep, sd_state, &format, - NULL, compose, &outcc); + csi_try_fmt(priv, &mbus_cfg, sd_state, &format, NULL, + compose, &outcc); outfmt = __csi_get_fmt(priv, sd_state, pad, sdformat->which); @@ -1648,7 +1638,7 @@ static int csi_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_selection *sel) { struct csi_priv *priv = v4l2_get_subdevdata(sd); - struct v4l2_fwnode_endpoint upstream_ep = { .bus_type = 0 }; + struct v4l2_mbus_config mbus_cfg = { .type = 0 }; struct v4l2_mbus_framefmt *infmt; struct v4l2_rect *crop, *compose; int pad, ret; @@ -1656,9 +1646,10 @@ static int csi_set_selection(struct v4l2_subdev *sd, if (sel->pad != CSI_SINK_PAD) return -EINVAL; - ret = csi_get_upstream_endpoint(priv, &upstream_ep); + ret = csi_get_upstream_mbus_config(priv, &mbus_cfg); if (ret) { - v4l2_err(&priv->sd, "failed to find upstream endpoint\n"); + v4l2_err(&priv->sd, + "failed to get upstream media bus configuration\n"); return ret; } @@ -1687,7 +1678,7 @@ static int csi_set_selection(struct v4l2_subdev *sd, goto out; } - csi_try_crop(priv, &sel->r, sd_state, infmt, &upstream_ep); + csi_try_crop(priv, &sel->r, sd_state, infmt, &mbus_cfg); *crop = sel->r; diff --git a/drivers/staging/media/imx/imx-media-fim.c b/drivers/staging/media/imx/imx-media-fim.c index 3a9182933508..fb6590dcfc36 100644 --- a/drivers/staging/media/imx/imx-media-fim.c +++ b/drivers/staging/media/imx/imx-media-fim.c @@ -187,54 +187,6 @@ out_update_ts: send_fim_event(fim, error_avg); } -#ifdef CONFIG_IMX_GPT_ICAP -/* - * Input Capture method of measuring frame intervals. Not subject - * to interrupt latency. - */ -static void fim_input_capture_handler(int channel, void *dev_id, - ktime_t timestamp) -{ - struct imx_media_fim *fim = dev_id; - unsigned long flags; - - spin_lock_irqsave(&fim->lock, flags); - - frame_interval_monitor(fim, timestamp); - - if (!completion_done(&fim->icap_first_event)) - complete(&fim->icap_first_event); - - spin_unlock_irqrestore(&fim->lock, flags); -} - -static int fim_request_input_capture(struct imx_media_fim *fim) -{ - init_completion(&fim->icap_first_event); - - return mxc_request_input_capture(fim->icap_channel, - fim_input_capture_handler, - fim->icap_flags, fim); -} - -static void fim_free_input_capture(struct imx_media_fim *fim) -{ - mxc_free_input_capture(fim->icap_channel, fim); -} - -#else /* CONFIG_IMX_GPT_ICAP */ - -static int fim_request_input_capture(struct imx_media_fim *fim) -{ - return 0; -} - -static void fim_free_input_capture(struct imx_media_fim *fim) -{ -} - -#endif /* CONFIG_IMX_GPT_ICAP */ - /* * In case we are monitoring the first frame interval after streamon * (when fim->num_skip = 0), we need a valid fim->last_ts before we @@ -434,15 +386,8 @@ int imx_media_fim_set_stream(struct imx_media_fim *fim, update_fim_nominal(fim, fi); spin_unlock_irqrestore(&fim->lock, flags); - if (icap_enabled(fim)) { - ret = fim_request_input_capture(fim); - if (ret) - goto out; - fim_acquire_first_ts(fim); - } - } else { if (icap_enabled(fim)) - fim_free_input_capture(fim); + fim_acquire_first_ts(fim); } fim->stream_on = on; diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 3e7462112649..411e907b68eb 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -814,39 +814,6 @@ imx_media_pipeline_video_device(struct media_entity *start_entity, EXPORT_SYMBOL_GPL(imx_media_pipeline_video_device); /* - * Find a fwnode endpoint that maps to the given subdevice's pad. - * If there are multiple endpoints that map to the pad, only the - * first endpoint encountered is returned. - * - * On success the refcount of the returned fwnode endpoint is - * incremented. - */ -struct fwnode_handle *imx_media_get_pad_fwnode(struct media_pad *pad) -{ - struct fwnode_handle *endpoint; - struct v4l2_subdev *sd; - - if (!is_media_entity_v4l2_subdev(pad->entity)) - return ERR_PTR(-ENODEV); - - sd = media_entity_to_v4l2_subdev(pad->entity); - - fwnode_graph_for_each_endpoint(dev_fwnode(sd->dev), endpoint) { - int pad_idx = media_entity_get_fwnode_pad(&sd->entity, - endpoint, - pad->flags); - if (pad_idx < 0) - continue; - - if (pad_idx == pad->index) - return endpoint; - } - - return ERR_PTR(-ENODEV); -} -EXPORT_SYMBOL_GPL(imx_media_get_pad_fwnode); - -/* * Turn current pipeline streaming on/off starting from entity. */ int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd, diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h index f263fc3adbb9..f679249d82e4 100644 --- a/drivers/staging/media/imx/imx-media.h +++ b/drivers/staging/media/imx/imx-media.h @@ -219,7 +219,6 @@ imx_media_pipeline_subdev(struct media_entity *start_entity, u32 grp_id, struct video_device * imx_media_pipeline_video_device(struct media_entity *start_entity, enum v4l2_buf_type buftype, bool upstream); -struct fwnode_handle *imx_media_get_pad_fwnode(struct media_pad *pad); struct imx_media_dma_buf { void *virt; diff --git a/drivers/staging/media/imx/imx7-media-csi.c b/drivers/staging/media/imx/imx7-media-csi.c deleted file mode 100644 index e5b550ccfa22..000000000000 --- a/drivers/staging/media/imx/imx7-media-csi.c +++ /dev/null @@ -1,2308 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * V4L2 Capture CSI Subdev for Freescale i.MX6UL/L / i.MX7 SOC - * - * Copyright (c) 2019 Linaro Ltd - * - */ - -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/gcd.h> -#include <linux/interrupt.h> -#include <linux/mfd/syscon.h> -#include <linux/module.h> -#include <linux/of_device.h> -#include <linux/of_graph.h> -#include <linux/pinctrl/consumer.h> -#include <linux/platform_device.h> -#include <linux/regmap.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/types.h> - -#include <media/v4l2-device.h> -#include <media/v4l2-fwnode.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-mc.h> -#include <media/v4l2-subdev.h> -#include <media/videobuf2-dma-contig.h> - -#define IMX7_CSI_PAD_SINK 0 -#define IMX7_CSI_PAD_SRC 1 -#define IMX7_CSI_PADS_NUM 2 - -/* csi control reg 1 */ -#define BIT_SWAP16_EN BIT(31) -#define BIT_EXT_VSYNC BIT(30) -#define BIT_EOF_INT_EN BIT(29) -#define BIT_PRP_IF_EN BIT(28) -#define BIT_CCIR_MODE BIT(27) -#define BIT_COF_INT_EN BIT(26) -#define BIT_SF_OR_INTEN BIT(25) -#define BIT_RF_OR_INTEN BIT(24) -#define BIT_SFF_DMA_DONE_INTEN BIT(22) -#define BIT_STATFF_INTEN BIT(21) -#define BIT_FB2_DMA_DONE_INTEN BIT(20) -#define BIT_FB1_DMA_DONE_INTEN BIT(19) -#define BIT_RXFF_INTEN BIT(18) -#define BIT_SOF_POL BIT(17) -#define BIT_SOF_INTEN BIT(16) -#define BIT_MCLKDIV(n) ((n) << 12) -#define BIT_MCLKDIV_MASK (0xf << 12) -#define BIT_HSYNC_POL BIT(11) -#define BIT_CCIR_EN BIT(10) -#define BIT_MCLKEN BIT(9) -#define BIT_FCC BIT(8) -#define BIT_PACK_DIR BIT(7) -#define BIT_CLR_STATFIFO BIT(6) -#define BIT_CLR_RXFIFO BIT(5) -#define BIT_GCLK_MODE BIT(4) -#define BIT_INV_DATA BIT(3) -#define BIT_INV_PCLK BIT(2) -#define BIT_REDGE BIT(1) -#define BIT_PIXEL_BIT BIT(0) - -/* control reg 2 */ -#define BIT_DMA_BURST_TYPE_RFF_INCR4 (1 << 30) -#define BIT_DMA_BURST_TYPE_RFF_INCR8 (2 << 30) -#define BIT_DMA_BURST_TYPE_RFF_INCR16 (3 << 30) -#define BIT_DMA_BURST_TYPE_RFF_MASK (3 << 30) - -/* control reg 3 */ -#define BIT_FRMCNT(n) ((n) << 16) -#define BIT_FRMCNT_MASK (0xffff << 16) -#define BIT_FRMCNT_RST BIT(15) -#define BIT_DMA_REFLASH_RFF BIT(14) -#define BIT_DMA_REFLASH_SFF BIT(13) -#define BIT_DMA_REQ_EN_RFF BIT(12) -#define BIT_DMA_REQ_EN_SFF BIT(11) -#define BIT_STATFF_LEVEL(n) ((n) << 8) -#define BIT_STATFF_LEVEL_MASK (0x7 << 8) -#define BIT_HRESP_ERR_EN BIT(7) -#define BIT_RXFF_LEVEL(n) ((n) << 4) -#define BIT_RXFF_LEVEL_MASK (0x7 << 4) -#define BIT_TWO_8BIT_SENSOR BIT(3) -#define BIT_ZERO_PACK_EN BIT(2) -#define BIT_ECC_INT_EN BIT(1) -#define BIT_ECC_AUTO_EN BIT(0) - -/* csi status reg */ -#define BIT_ADDR_CH_ERR_INT BIT(28) -#define BIT_FIELD0_INT BIT(27) -#define BIT_FIELD1_INT BIT(26) -#define BIT_SFF_OR_INT BIT(25) -#define BIT_RFF_OR_INT BIT(24) -#define BIT_DMA_TSF_DONE_SFF BIT(22) -#define BIT_STATFF_INT BIT(21) -#define BIT_DMA_TSF_DONE_FB2 BIT(20) -#define BIT_DMA_TSF_DONE_FB1 BIT(19) -#define BIT_RXFF_INT BIT(18) -#define BIT_EOF_INT BIT(17) -#define BIT_SOF_INT BIT(16) -#define BIT_F2_INT BIT(15) -#define BIT_F1_INT BIT(14) -#define BIT_COF_INT BIT(13) -#define BIT_HRESP_ERR_INT BIT(7) -#define BIT_ECC_INT BIT(1) -#define BIT_DRDY BIT(0) - -/* csi image parameter reg */ -#define BIT_IMAGE_WIDTH(n) ((n) << 16) -#define BIT_IMAGE_HEIGHT(n) (n) - -/* csi control reg 18 */ -#define BIT_CSI_HW_ENABLE BIT(31) -#define BIT_MIPI_DATA_FORMAT_RAW8 (0x2a << 25) -#define BIT_MIPI_DATA_FORMAT_RAW10 (0x2b << 25) -#define BIT_MIPI_DATA_FORMAT_RAW12 (0x2c << 25) -#define BIT_MIPI_DATA_FORMAT_RAW14 (0x2d << 25) -#define BIT_MIPI_DATA_FORMAT_YUV422_8B (0x1e << 25) -#define BIT_MIPI_DATA_FORMAT_MASK (0x3f << 25) -#define BIT_DATA_FROM_MIPI BIT(22) -#define BIT_MIPI_YU_SWAP BIT(21) -#define BIT_MIPI_DOUBLE_CMPNT BIT(20) -#define BIT_MASK_OPTION_FIRST_FRAME (0 << 18) -#define BIT_MASK_OPTION_CSI_EN (1 << 18) -#define BIT_MASK_OPTION_SECOND_FRAME (2 << 18) -#define BIT_MASK_OPTION_ON_DATA (3 << 18) -#define BIT_BASEADDR_CHG_ERR_EN BIT(9) -#define BIT_BASEADDR_SWITCH_SEL BIT(5) -#define BIT_BASEADDR_SWITCH_EN BIT(4) -#define BIT_PARALLEL24_EN BIT(3) -#define BIT_DEINTERLACE_EN BIT(2) -#define BIT_TVDECODER_IN_EN BIT(1) -#define BIT_NTSC_EN BIT(0) - -#define CSI_MCLK_VF 1 -#define CSI_MCLK_ENC 2 -#define CSI_MCLK_RAW 4 -#define CSI_MCLK_I2C 8 - -#define CSI_CSICR1 0x00 -#define CSI_CSICR2 0x04 -#define CSI_CSICR3 0x08 -#define CSI_STATFIFO 0x0c -#define CSI_CSIRXFIFO 0x10 -#define CSI_CSIRXCNT 0x14 -#define CSI_CSISR 0x18 - -#define CSI_CSIDBG 0x1c -#define CSI_CSIDMASA_STATFIFO 0x20 -#define CSI_CSIDMATS_STATFIFO 0x24 -#define CSI_CSIDMASA_FB1 0x28 -#define CSI_CSIDMASA_FB2 0x2c -#define CSI_CSIFBUF_PARA 0x30 -#define CSI_CSIIMAG_PARA 0x34 - -#define CSI_CSICR18 0x48 -#define CSI_CSICR19 0x4c - -#define IMX7_CSI_VIDEO_NAME "imx-capture" -/* In bytes, per queue */ -#define IMX7_CSI_VIDEO_MEM_LIMIT SZ_512M -#define IMX7_CSI_VIDEO_EOF_TIMEOUT 2000 - -#define IMX7_CSI_DEF_MBUS_CODE MEDIA_BUS_FMT_UYVY8_2X8 -#define IMX7_CSI_DEF_PIX_FORMAT V4L2_PIX_FMT_UYVY -#define IMX7_CSI_DEF_PIX_WIDTH 640 -#define IMX7_CSI_DEF_PIX_HEIGHT 480 - -enum imx_csi_model { - IMX7_CSI_IMX7 = 0, - IMX7_CSI_IMX8MQ, -}; - -struct imx7_csi_pixfmt { - /* the in-memory FourCC pixel format */ - u32 fourcc; - /* - * the set of equivalent media bus codes for the fourcc. - * NOTE! codes pointer is NULL for in-memory-only formats. - */ - const u32 *codes; - int bpp; /* total bpp */ - bool yuv; -}; - -struct imx7_csi_vb2_buffer { - struct vb2_v4l2_buffer vbuf; - struct list_head list; -}; - -static inline struct imx7_csi_vb2_buffer * -to_imx7_csi_vb2_buffer(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - - return container_of(vbuf, struct imx7_csi_vb2_buffer, vbuf); -} - -struct imx7_csi_dma_buf { - void *virt; - dma_addr_t phys; - unsigned long len; -}; - -struct imx7_csi { - struct device *dev; - - /* Resources and locks */ - void __iomem *regbase; - int irq; - struct clk *mclk; - - struct mutex lock; /* Protects is_streaming, format_mbus, cc */ - spinlock_t irqlock; /* Protects last_eof */ - - /* Media and V4L2 device */ - struct media_device mdev; - struct v4l2_device v4l2_dev; - struct v4l2_async_notifier notifier; - struct media_pipeline pipe; - - struct v4l2_subdev *src_sd; - bool is_csi2; - - /* V4L2 subdev */ - struct v4l2_subdev sd; - struct media_pad pad[IMX7_CSI_PADS_NUM]; - - struct v4l2_mbus_framefmt format_mbus[IMX7_CSI_PADS_NUM]; - const struct imx7_csi_pixfmt *cc[IMX7_CSI_PADS_NUM]; - - /* Video device */ - struct video_device *vdev; /* Video device */ - struct media_pad vdev_pad; /* Video device pad */ - - struct v4l2_pix_format vdev_fmt; /* The user format */ - const struct imx7_csi_pixfmt *vdev_cc; - struct v4l2_rect vdev_compose; /* The compose rectangle */ - - struct mutex vdev_mutex; /* Protect vdev operations */ - - struct vb2_queue q; /* The videobuf2 queue */ - struct list_head ready_q; /* List of queued buffers */ - spinlock_t q_lock; /* Protect ready_q */ - - /* Buffers and streaming state */ - struct imx7_csi_vb2_buffer *active_vb2_buf[2]; - struct imx7_csi_dma_buf underrun_buf; - - bool is_streaming; - int buf_num; - u32 frame_sequence; - - bool last_eof; - struct completion last_eof_completion; - - enum imx_csi_model model; -}; - -static struct imx7_csi * -imx7_csi_notifier_to_dev(struct v4l2_async_notifier *n) -{ - return container_of(n, struct imx7_csi, notifier); -} - -/* ----------------------------------------------------------------------------- - * Hardware Configuration - */ - -static u32 imx7_csi_reg_read(struct imx7_csi *csi, unsigned int offset) -{ - return readl(csi->regbase + offset); -} - -static void imx7_csi_reg_write(struct imx7_csi *csi, unsigned int value, - unsigned int offset) -{ - writel(value, csi->regbase + offset); -} - -static u32 imx7_csi_irq_clear(struct imx7_csi *csi) -{ - u32 isr; - - isr = imx7_csi_reg_read(csi, CSI_CSISR); - imx7_csi_reg_write(csi, isr, CSI_CSISR); - - return isr; -} - -static void imx7_csi_init_default(struct imx7_csi *csi) -{ - imx7_csi_reg_write(csi, BIT_SOF_POL | BIT_REDGE | BIT_GCLK_MODE | - BIT_HSYNC_POL | BIT_FCC | BIT_MCLKDIV(1) | - BIT_MCLKEN, CSI_CSICR1); - imx7_csi_reg_write(csi, 0, CSI_CSICR2); - imx7_csi_reg_write(csi, BIT_FRMCNT_RST, CSI_CSICR3); - - imx7_csi_reg_write(csi, BIT_IMAGE_WIDTH(IMX7_CSI_DEF_PIX_WIDTH) | - BIT_IMAGE_HEIGHT(IMX7_CSI_DEF_PIX_HEIGHT), - CSI_CSIIMAG_PARA); - - imx7_csi_reg_write(csi, BIT_DMA_REFLASH_RFF, CSI_CSICR3); -} - -static void imx7_csi_hw_enable_irq(struct imx7_csi *csi) -{ - u32 cr1 = imx7_csi_reg_read(csi, CSI_CSICR1); - - cr1 |= BIT_RFF_OR_INT; - cr1 |= BIT_FB1_DMA_DONE_INTEN; - cr1 |= BIT_FB2_DMA_DONE_INTEN; - - imx7_csi_reg_write(csi, cr1, CSI_CSICR1); -} - -static void imx7_csi_hw_disable_irq(struct imx7_csi *csi) -{ - u32 cr1 = imx7_csi_reg_read(csi, CSI_CSICR1); - - cr1 &= ~BIT_RFF_OR_INT; - cr1 &= ~BIT_FB1_DMA_DONE_INTEN; - cr1 &= ~BIT_FB2_DMA_DONE_INTEN; - - imx7_csi_reg_write(csi, cr1, CSI_CSICR1); -} - -static void imx7_csi_hw_enable(struct imx7_csi *csi) -{ - u32 cr = imx7_csi_reg_read(csi, CSI_CSICR18); - - cr |= BIT_CSI_HW_ENABLE; - - imx7_csi_reg_write(csi, cr, CSI_CSICR18); -} - -static void imx7_csi_hw_disable(struct imx7_csi *csi) -{ - u32 cr = imx7_csi_reg_read(csi, CSI_CSICR18); - - cr &= ~BIT_CSI_HW_ENABLE; - - imx7_csi_reg_write(csi, cr, CSI_CSICR18); -} - -static void imx7_csi_dma_reflash(struct imx7_csi *csi) -{ - u32 cr3; - - cr3 = imx7_csi_reg_read(csi, CSI_CSICR3); - cr3 |= BIT_DMA_REFLASH_RFF; - imx7_csi_reg_write(csi, cr3, CSI_CSICR3); -} - -static void imx7_csi_rx_fifo_clear(struct imx7_csi *csi) -{ - u32 cr1 = imx7_csi_reg_read(csi, CSI_CSICR1) & ~BIT_FCC; - - imx7_csi_reg_write(csi, cr1, CSI_CSICR1); - imx7_csi_reg_write(csi, cr1 | BIT_CLR_RXFIFO, CSI_CSICR1); - imx7_csi_reg_write(csi, cr1 | BIT_FCC, CSI_CSICR1); -} - -static void imx7_csi_dmareq_rff_enable(struct imx7_csi *csi) -{ - u32 cr3 = imx7_csi_reg_read(csi, CSI_CSICR3); - - cr3 |= BIT_DMA_REQ_EN_RFF; - cr3 |= BIT_HRESP_ERR_EN; - cr3 &= ~BIT_RXFF_LEVEL_MASK; - cr3 |= BIT_RXFF_LEVEL(2); - - imx7_csi_reg_write(csi, cr3, CSI_CSICR3); -} - -static void imx7_csi_dmareq_rff_disable(struct imx7_csi *csi) -{ - u32 cr3 = imx7_csi_reg_read(csi, CSI_CSICR3); - - cr3 &= ~BIT_DMA_REQ_EN_RFF; - cr3 &= ~BIT_HRESP_ERR_EN; - imx7_csi_reg_write(csi, cr3, CSI_CSICR3); -} - -static void imx7_csi_update_buf(struct imx7_csi *csi, dma_addr_t phys, - int buf_num) -{ - if (buf_num == 1) - imx7_csi_reg_write(csi, phys, CSI_CSIDMASA_FB2); - else - imx7_csi_reg_write(csi, phys, CSI_CSIDMASA_FB1); -} - -static struct imx7_csi_vb2_buffer *imx7_csi_video_next_buf(struct imx7_csi *csi); - -static void imx7_csi_setup_vb2_buf(struct imx7_csi *csi) -{ - struct imx7_csi_vb2_buffer *buf; - struct vb2_buffer *vb2_buf; - dma_addr_t phys[2]; - int i; - - for (i = 0; i < 2; i++) { - buf = imx7_csi_video_next_buf(csi); - if (buf) { - csi->active_vb2_buf[i] = buf; - vb2_buf = &buf->vbuf.vb2_buf; - phys[i] = vb2_dma_contig_plane_dma_addr(vb2_buf, 0); - } else { - csi->active_vb2_buf[i] = NULL; - phys[i] = csi->underrun_buf.phys; - } - - imx7_csi_update_buf(csi, phys[i], i); - } -} - -static void imx7_csi_dma_unsetup_vb2_buf(struct imx7_csi *csi, - enum vb2_buffer_state return_status) -{ - struct imx7_csi_vb2_buffer *buf; - int i; - - /* return any remaining active frames with return_status */ - for (i = 0; i < 2; i++) { - buf = csi->active_vb2_buf[i]; - if (buf) { - struct vb2_buffer *vb = &buf->vbuf.vb2_buf; - - vb->timestamp = ktime_get_ns(); - vb2_buffer_done(vb, return_status); - csi->active_vb2_buf[i] = NULL; - } - } -} - -static void imx7_csi_free_dma_buf(struct imx7_csi *csi, - struct imx7_csi_dma_buf *buf) -{ - if (buf->virt) - dma_free_coherent(csi->dev, buf->len, buf->virt, buf->phys); - - buf->virt = NULL; - buf->phys = 0; -} - -static int imx7_csi_alloc_dma_buf(struct imx7_csi *csi, - struct imx7_csi_dma_buf *buf, int size) -{ - imx7_csi_free_dma_buf(csi, buf); - - buf->len = PAGE_ALIGN(size); - buf->virt = dma_alloc_coherent(csi->dev, buf->len, &buf->phys, - GFP_DMA | GFP_KERNEL); - if (!buf->virt) - return -ENOMEM; - - return 0; -} - -static int imx7_csi_dma_setup(struct imx7_csi *csi) -{ - int ret; - - ret = imx7_csi_alloc_dma_buf(csi, &csi->underrun_buf, - csi->vdev_fmt.sizeimage); - if (ret < 0) { - v4l2_warn(&csi->sd, "consider increasing the CMA area\n"); - return ret; - } - - csi->frame_sequence = 0; - csi->last_eof = false; - init_completion(&csi->last_eof_completion); - - imx7_csi_setup_vb2_buf(csi); - - return 0; -} - -static void imx7_csi_dma_cleanup(struct imx7_csi *csi, - enum vb2_buffer_state return_status) -{ - imx7_csi_dma_unsetup_vb2_buf(csi, return_status); - imx7_csi_free_dma_buf(csi, &csi->underrun_buf); -} - -static void imx7_csi_dma_stop(struct imx7_csi *csi) -{ - unsigned long timeout_jiffies; - unsigned long flags; - int ret; - - /* mark next EOF interrupt as the last before stream off */ - spin_lock_irqsave(&csi->irqlock, flags); - csi->last_eof = true; - spin_unlock_irqrestore(&csi->irqlock, flags); - - /* - * and then wait for interrupt handler to mark completion. - */ - timeout_jiffies = msecs_to_jiffies(IMX7_CSI_VIDEO_EOF_TIMEOUT); - ret = wait_for_completion_timeout(&csi->last_eof_completion, - timeout_jiffies); - if (ret == 0) - v4l2_warn(&csi->sd, "wait last EOF timeout\n"); - - imx7_csi_hw_disable_irq(csi); -} - -static void imx7_csi_configure(struct imx7_csi *csi) -{ - struct v4l2_pix_format *out_pix = &csi->vdev_fmt; - int width = out_pix->width; - u32 stride = 0; - u32 cr3 = BIT_FRMCNT_RST; - u32 cr1, cr18; - - cr18 = imx7_csi_reg_read(csi, CSI_CSICR18); - - cr18 &= ~(BIT_CSI_HW_ENABLE | BIT_MIPI_DATA_FORMAT_MASK | - BIT_DATA_FROM_MIPI | BIT_BASEADDR_CHG_ERR_EN | - BIT_BASEADDR_SWITCH_EN | BIT_BASEADDR_SWITCH_SEL | - BIT_DEINTERLACE_EN); - - if (out_pix->field == V4L2_FIELD_INTERLACED) { - cr18 |= BIT_DEINTERLACE_EN; - stride = out_pix->width; - } - - if (!csi->is_csi2) { - cr1 = BIT_SOF_POL | BIT_REDGE | BIT_GCLK_MODE | BIT_HSYNC_POL - | BIT_FCC | BIT_MCLKDIV(1) | BIT_MCLKEN; - - cr18 |= BIT_BASEADDR_SWITCH_EN | BIT_BASEADDR_SWITCH_SEL | - BIT_BASEADDR_CHG_ERR_EN; - - if (out_pix->pixelformat == V4L2_PIX_FMT_UYVY || - out_pix->pixelformat == V4L2_PIX_FMT_YUYV) - width *= 2; - } else { - cr1 = BIT_SOF_POL | BIT_REDGE | BIT_HSYNC_POL | BIT_FCC - | BIT_MCLKDIV(1) | BIT_MCLKEN; - - cr18 |= BIT_DATA_FROM_MIPI; - - switch (csi->format_mbus[IMX7_CSI_PAD_SINK].code) { - case MEDIA_BUS_FMT_Y8_1X8: - case MEDIA_BUS_FMT_SBGGR8_1X8: - case MEDIA_BUS_FMT_SGBRG8_1X8: - case MEDIA_BUS_FMT_SGRBG8_1X8: - case MEDIA_BUS_FMT_SRGGB8_1X8: - cr18 |= BIT_MIPI_DATA_FORMAT_RAW8; - break; - case MEDIA_BUS_FMT_Y10_1X10: - case MEDIA_BUS_FMT_SBGGR10_1X10: - case MEDIA_BUS_FMT_SGBRG10_1X10: - case MEDIA_BUS_FMT_SGRBG10_1X10: - case MEDIA_BUS_FMT_SRGGB10_1X10: - cr3 |= BIT_TWO_8BIT_SENSOR; - cr18 |= BIT_MIPI_DATA_FORMAT_RAW10; - break; - case MEDIA_BUS_FMT_Y12_1X12: - case MEDIA_BUS_FMT_SBGGR12_1X12: - case MEDIA_BUS_FMT_SGBRG12_1X12: - case MEDIA_BUS_FMT_SGRBG12_1X12: - case MEDIA_BUS_FMT_SRGGB12_1X12: - cr3 |= BIT_TWO_8BIT_SENSOR; - cr18 |= BIT_MIPI_DATA_FORMAT_RAW12; - break; - case MEDIA_BUS_FMT_Y14_1X14: - case MEDIA_BUS_FMT_SBGGR14_1X14: - case MEDIA_BUS_FMT_SGBRG14_1X14: - case MEDIA_BUS_FMT_SGRBG14_1X14: - case MEDIA_BUS_FMT_SRGGB14_1X14: - cr3 |= BIT_TWO_8BIT_SENSOR; - cr18 |= BIT_MIPI_DATA_FORMAT_RAW14; - break; - - /* - * The CSI bridge has a 16-bit input bus. Depending on the - * connected source, data may be transmitted with 8 or 10 bits - * per clock sample (in bits [9:2] or [9:0] respectively) or - * with 16 bits per clock sample (in bits [15:0]). The data is - * then packed into a 32-bit FIFO (as shown in figure 13-11 of - * the i.MX8MM reference manual rev. 3). - * - * The data packing in a 32-bit FIFO input word is controlled by - * the CR3 TWO_8BIT_SENSOR field (also known as SENSOR_16BITS in - * the i.MX8MM reference manual). When set to 0, data packing - * groups four 8-bit input samples (bits [9:2]). When set to 1, - * data packing groups two 16-bit input samples (bits [15:0]). - * - * The register field CR18 MIPI_DOUBLE_CMPNT also needs to be - * configured according to the input format for YUV 4:2:2 data. - * The field controls the gasket between the CSI-2 receiver and - * the CSI bridge. On i.MX7 and i.MX8MM, the field must be set - * to 1 when the CSIS outputs 16-bit samples. On i.MX8MQ, the - * gasket ignores the MIPI_DOUBLE_CMPNT bit and YUV 4:2:2 always - * uses 16-bit samples. Setting MIPI_DOUBLE_CMPNT in that case - * has no effect, but doesn't cause any issue. - */ - case MEDIA_BUS_FMT_UYVY8_2X8: - case MEDIA_BUS_FMT_YUYV8_2X8: - cr18 |= BIT_MIPI_DATA_FORMAT_YUV422_8B; - break; - case MEDIA_BUS_FMT_UYVY8_1X16: - case MEDIA_BUS_FMT_YUYV8_1X16: - cr3 |= BIT_TWO_8BIT_SENSOR; - cr18 |= BIT_MIPI_DATA_FORMAT_YUV422_8B | - BIT_MIPI_DOUBLE_CMPNT; - break; - } - } - - imx7_csi_reg_write(csi, cr1, CSI_CSICR1); - imx7_csi_reg_write(csi, BIT_DMA_BURST_TYPE_RFF_INCR16, CSI_CSICR2); - imx7_csi_reg_write(csi, cr3, CSI_CSICR3); - imx7_csi_reg_write(csi, cr18, CSI_CSICR18); - - imx7_csi_reg_write(csi, (width * out_pix->height) >> 2, CSI_CSIRXCNT); - imx7_csi_reg_write(csi, BIT_IMAGE_WIDTH(width) | - BIT_IMAGE_HEIGHT(out_pix->height), - CSI_CSIIMAG_PARA); - imx7_csi_reg_write(csi, stride, CSI_CSIFBUF_PARA); -} - -static int imx7_csi_init(struct imx7_csi *csi) -{ - int ret; - - ret = clk_prepare_enable(csi->mclk); - if (ret < 0) - return ret; - - imx7_csi_configure(csi); - - ret = imx7_csi_dma_setup(csi); - if (ret < 0) - return ret; - - return 0; -} - -static void imx7_csi_deinit(struct imx7_csi *csi, - enum vb2_buffer_state return_status) -{ - imx7_csi_dma_cleanup(csi, return_status); - imx7_csi_init_default(csi); - imx7_csi_dmareq_rff_disable(csi); - clk_disable_unprepare(csi->mclk); -} - -static void imx7_csi_baseaddr_switch_on_second_frame(struct imx7_csi *csi) -{ - u32 cr18 = imx7_csi_reg_read(csi, CSI_CSICR18); - - cr18 |= BIT_BASEADDR_SWITCH_EN | BIT_BASEADDR_SWITCH_SEL | - BIT_BASEADDR_CHG_ERR_EN; - cr18 |= BIT_MASK_OPTION_SECOND_FRAME; - imx7_csi_reg_write(csi, cr18, CSI_CSICR18); -} - -static void imx7_csi_enable(struct imx7_csi *csi) -{ - /* Clear the Rx FIFO and reflash the DMA controller. */ - imx7_csi_rx_fifo_clear(csi); - imx7_csi_dma_reflash(csi); - - usleep_range(2000, 3000); - - /* Clear and enable the interrupts. */ - imx7_csi_irq_clear(csi); - imx7_csi_hw_enable_irq(csi); - - /* Enable the RxFIFO DMA and the CSI. */ - imx7_csi_dmareq_rff_enable(csi); - imx7_csi_hw_enable(csi); - - if (csi->model == IMX7_CSI_IMX8MQ) - imx7_csi_baseaddr_switch_on_second_frame(csi); -} - -static void imx7_csi_disable(struct imx7_csi *csi) -{ - imx7_csi_dma_stop(csi); - - imx7_csi_dmareq_rff_disable(csi); - - imx7_csi_hw_disable_irq(csi); - - imx7_csi_hw_disable(csi); -} - -/* ----------------------------------------------------------------------------- - * Interrupt Handling - */ - -static void imx7_csi_error_recovery(struct imx7_csi *csi) -{ - imx7_csi_hw_disable(csi); - - imx7_csi_rx_fifo_clear(csi); - - imx7_csi_dma_reflash(csi); - - imx7_csi_hw_enable(csi); -} - -static void imx7_csi_vb2_buf_done(struct imx7_csi *csi) -{ - struct imx7_csi_vb2_buffer *done, *next; - struct vb2_buffer *vb; - dma_addr_t phys; - - done = csi->active_vb2_buf[csi->buf_num]; - if (done) { - done->vbuf.field = csi->vdev_fmt.field; - done->vbuf.sequence = csi->frame_sequence; - vb = &done->vbuf.vb2_buf; - vb->timestamp = ktime_get_ns(); - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); - } - csi->frame_sequence++; - - /* get next queued buffer */ - next = imx7_csi_video_next_buf(csi); - if (next) { - phys = vb2_dma_contig_plane_dma_addr(&next->vbuf.vb2_buf, 0); - csi->active_vb2_buf[csi->buf_num] = next; - } else { - phys = csi->underrun_buf.phys; - csi->active_vb2_buf[csi->buf_num] = NULL; - } - - imx7_csi_update_buf(csi, phys, csi->buf_num); -} - -static irqreturn_t imx7_csi_irq_handler(int irq, void *data) -{ - struct imx7_csi *csi = data; - u32 status; - - spin_lock(&csi->irqlock); - - status = imx7_csi_irq_clear(csi); - - if (status & BIT_RFF_OR_INT) { - dev_warn(csi->dev, "Rx fifo overflow\n"); - imx7_csi_error_recovery(csi); - } - - if (status & BIT_HRESP_ERR_INT) { - dev_warn(csi->dev, "Hresponse error detected\n"); - imx7_csi_error_recovery(csi); - } - - if (status & BIT_ADDR_CH_ERR_INT) { - imx7_csi_hw_disable(csi); - - imx7_csi_dma_reflash(csi); - - imx7_csi_hw_enable(csi); - } - - if ((status & BIT_DMA_TSF_DONE_FB1) && - (status & BIT_DMA_TSF_DONE_FB2)) { - /* - * For both FB1 and FB2 interrupter bits set case, - * CSI DMA is work in one of FB1 and FB2 buffer, - * but software can not know the state. - * Skip it to avoid base address updated - * when csi work in field0 and field1 will write to - * new base address. - */ - } else if (status & BIT_DMA_TSF_DONE_FB1) { - csi->buf_num = 0; - } else if (status & BIT_DMA_TSF_DONE_FB2) { - csi->buf_num = 1; - } - - if ((status & BIT_DMA_TSF_DONE_FB1) || - (status & BIT_DMA_TSF_DONE_FB2)) { - imx7_csi_vb2_buf_done(csi); - - if (csi->last_eof) { - complete(&csi->last_eof_completion); - csi->last_eof = false; - } - } - - spin_unlock(&csi->irqlock); - - return IRQ_HANDLED; -} - -/* ----------------------------------------------------------------------------- - * Format Helpers - */ - -#define IMX_BUS_FMTS(fmt...) (const u32[]) {fmt, 0} - -/* - * List of supported pixel formats for the subdevs. Keep V4L2_PIX_FMT_UYVY and - * MEDIA_BUS_FMT_UYVY8_2X8 first to match IMX7_CSI_DEF_PIX_FORMAT and - * IMX7_CSI_DEF_MBUS_CODE. - */ -static const struct imx7_csi_pixfmt pixel_formats[] = { - /*** YUV formats start here ***/ - { - .fourcc = V4L2_PIX_FMT_UYVY, - .codes = IMX_BUS_FMTS( - MEDIA_BUS_FMT_UYVY8_2X8, - MEDIA_BUS_FMT_UYVY8_1X16 - ), - .yuv = true, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_YUYV, - .codes = IMX_BUS_FMTS( - MEDIA_BUS_FMT_YUYV8_2X8, - MEDIA_BUS_FMT_YUYV8_1X16 - ), - .yuv = true, - .bpp = 16, - }, - /*** raw bayer and grayscale formats start here ***/ - { - .fourcc = V4L2_PIX_FMT_SBGGR8, - .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SBGGR8_1X8), - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG8, - .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGBRG8_1X8), - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG8, - .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGRBG8_1X8), - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB8, - .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SRGGB8_1X8), - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SBGGR10_1X10), - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG10, - .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGBRG10_1X10), - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG10, - .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGRBG10_1X10), - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB10, - .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SRGGB10_1X10), - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR12, - .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SBGGR12_1X12), - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG12, - .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGBRG12_1X12), - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG12, - .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGRBG12_1X12), - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB12, - .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SRGGB12_1X12), - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR14, - .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SBGGR14_1X14), - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG14, - .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGBRG14_1X14), - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG14, - .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGRBG14_1X14), - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB14, - .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SRGGB14_1X14), - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_GREY, - .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_Y8_1X8), - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_Y10, - .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_Y10_1X10), - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_Y12, - .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_Y12_1X12), - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_Y14, - .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_Y14_1X14), - .bpp = 16, - }, -}; - -/* - * Search in the pixel_formats[] array for an entry with the given fourcc - * return it. - */ -static const struct imx7_csi_pixfmt *imx7_csi_find_pixel_format(u32 fourcc) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) { - const struct imx7_csi_pixfmt *fmt = &pixel_formats[i]; - - if (fmt->fourcc == fourcc) - return fmt; - } - - return NULL; -} - -/* - * Search in the pixel_formats[] array for an entry with the given media - * bus code and return it. - */ -static const struct imx7_csi_pixfmt *imx7_csi_find_mbus_format(u32 code) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) { - const struct imx7_csi_pixfmt *fmt = &pixel_formats[i]; - unsigned int j; - - if (!fmt->codes) - continue; - - for (j = 0; fmt->codes[j]; j++) { - if (code == fmt->codes[j]) - return fmt; - } - } - - return NULL; -} - -/* - * Enumerate entries in the pixel_formats[] array that match the - * requested search criteria. Return the media-bus code that matches - * the search criteria at the requested match index. - * - * @code: The returned media-bus code that matches the search criteria at - * the requested match index. - * @index: The requested match index. - */ -static int imx7_csi_enum_mbus_formats(u32 *code, u32 index) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) { - const struct imx7_csi_pixfmt *fmt = &pixel_formats[i]; - unsigned int j; - - if (!fmt->codes) - continue; - - for (j = 0; fmt->codes[j]; j++) { - if (index == 0) { - *code = fmt->codes[j]; - return 0; - } - - index--; - } - } - - return -EINVAL; -} - -static int imx7_csi_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix, - const struct v4l2_mbus_framefmt *mbus, - const struct imx7_csi_pixfmt *cc) -{ - u32 width; - u32 stride; - - if (!cc) { - cc = imx7_csi_find_mbus_format(mbus->code); - if (!cc) - return -EINVAL; - } - - /* Round up width for minimum burst size */ - width = round_up(mbus->width, 8); - - /* Round up stride for IDMAC line start address alignment */ - stride = round_up((width * cc->bpp) >> 3, 8); - - pix->width = width; - pix->height = mbus->height; - pix->pixelformat = cc->fourcc; - pix->colorspace = mbus->colorspace; - pix->xfer_func = mbus->xfer_func; - pix->ycbcr_enc = mbus->ycbcr_enc; - pix->quantization = mbus->quantization; - pix->field = mbus->field; - pix->bytesperline = stride; - pix->sizeimage = stride * pix->height; - - return 0; -} - -/* ----------------------------------------------------------------------------- - * Video Capture Device - IOCTLs - */ - -static int imx7_csi_video_querycap(struct file *file, void *fh, - struct v4l2_capability *cap) -{ - struct imx7_csi *csi = video_drvdata(file); - - strscpy(cap->driver, IMX7_CSI_VIDEO_NAME, sizeof(cap->driver)); - strscpy(cap->card, IMX7_CSI_VIDEO_NAME, sizeof(cap->card)); - snprintf(cap->bus_info, sizeof(cap->bus_info), - "platform:%s", dev_name(csi->dev)); - - return 0; -} - -static int imx7_csi_video_enum_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_fmtdesc *f) -{ - unsigned int index = f->index; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) { - const struct imx7_csi_pixfmt *fmt = &pixel_formats[i]; - - /* - * If a media bus code is specified, only consider formats that - * match it. - */ - if (f->mbus_code) { - unsigned int j; - - if (!fmt->codes) - continue; - - for (j = 0; fmt->codes[j]; j++) { - if (f->mbus_code == fmt->codes[j]) - break; - } - - if (!fmt->codes[j]) - continue; - } - - if (index == 0) { - f->pixelformat = fmt->fourcc; - return 0; - } - - index--; - } - - return -EINVAL; -} - -static int imx7_csi_video_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - const struct imx7_csi_pixfmt *cc; - - if (fsize->index > 0) - return -EINVAL; - - cc = imx7_csi_find_pixel_format(fsize->pixel_format); - if (!cc) - return -EINVAL; - - /* - * TODO: The constraints are hardware-specific and may depend on the - * pixel format. This should come from the driver using - * imx_media_capture. - */ - fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; - fsize->stepwise.min_width = 1; - fsize->stepwise.max_width = 65535; - fsize->stepwise.min_height = 1; - fsize->stepwise.max_height = 65535; - fsize->stepwise.step_width = 1; - fsize->stepwise.step_height = 1; - - return 0; -} - -static int imx7_csi_video_g_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct imx7_csi *csi = video_drvdata(file); - - f->fmt.pix = csi->vdev_fmt; - - return 0; -} - -static const struct imx7_csi_pixfmt * -__imx7_csi_video_try_fmt(struct v4l2_pix_format *pixfmt, - struct v4l2_rect *compose) -{ - struct v4l2_mbus_framefmt fmt_src; - const struct imx7_csi_pixfmt *cc; - - /* - * Find the pixel format, default to the first supported format if not - * found. - */ - cc = imx7_csi_find_pixel_format(pixfmt->pixelformat); - if (!cc) { - pixfmt->pixelformat = IMX7_CSI_DEF_PIX_FORMAT; - cc = imx7_csi_find_pixel_format(pixfmt->pixelformat); - } - - /* Allow IDMAC interweave but enforce field order from source. */ - if (V4L2_FIELD_IS_INTERLACED(pixfmt->field)) { - switch (pixfmt->field) { - case V4L2_FIELD_SEQ_TB: - pixfmt->field = V4L2_FIELD_INTERLACED_TB; - break; - case V4L2_FIELD_SEQ_BT: - pixfmt->field = V4L2_FIELD_INTERLACED_BT; - break; - default: - break; - } - } - - v4l2_fill_mbus_format(&fmt_src, pixfmt, 0); - imx7_csi_mbus_fmt_to_pix_fmt(pixfmt, &fmt_src, cc); - - if (compose) { - compose->width = fmt_src.width; - compose->height = fmt_src.height; - } - - return cc; -} - -static int imx7_csi_video_try_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_format *f) -{ - __imx7_csi_video_try_fmt(&f->fmt.pix, NULL); - return 0; -} - -static int imx7_csi_video_s_fmt_vid_cap(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct imx7_csi *csi = video_drvdata(file); - const struct imx7_csi_pixfmt *cc; - - if (vb2_is_busy(&csi->q)) { - dev_err(csi->dev, "%s queue busy\n", __func__); - return -EBUSY; - } - - cc = __imx7_csi_video_try_fmt(&f->fmt.pix, &csi->vdev_compose); - - csi->vdev_cc = cc; - csi->vdev_fmt = f->fmt.pix; - - return 0; -} - -static int imx7_csi_video_g_selection(struct file *file, void *fh, - struct v4l2_selection *s) -{ - struct imx7_csi *csi = video_drvdata(file); - - switch (s->target) { - case V4L2_SEL_TGT_COMPOSE: - case V4L2_SEL_TGT_COMPOSE_DEFAULT: - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - /* The compose rectangle is fixed to the source format. */ - s->r = csi->vdev_compose; - break; - case V4L2_SEL_TGT_COMPOSE_PADDED: - /* - * The hardware writes with a configurable but fixed DMA burst - * size. If the source format width is not burst size aligned, - * the written frame contains padding to the right. - */ - s->r.left = 0; - s->r.top = 0; - s->r.width = csi->vdev_fmt.width; - s->r.height = csi->vdev_fmt.height; - break; - default: - return -EINVAL; - } - - return 0; -} - -static const struct v4l2_ioctl_ops imx7_csi_video_ioctl_ops = { - .vidioc_querycap = imx7_csi_video_querycap, - - .vidioc_enum_fmt_vid_cap = imx7_csi_video_enum_fmt_vid_cap, - .vidioc_enum_framesizes = imx7_csi_video_enum_framesizes, - - .vidioc_g_fmt_vid_cap = imx7_csi_video_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = imx7_csi_video_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = imx7_csi_video_s_fmt_vid_cap, - - .vidioc_g_selection = imx7_csi_video_g_selection, - - .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_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, -}; - -/* ----------------------------------------------------------------------------- - * Video Capture Device - Queue Operations - */ - -static int imx7_csi_video_queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, - unsigned int *nplanes, - unsigned int sizes[], - struct device *alloc_devs[]) -{ - struct imx7_csi *csi = vb2_get_drv_priv(vq); - struct v4l2_pix_format *pix = &csi->vdev_fmt; - unsigned int count = *nbuffers; - - if (vq->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (*nplanes) { - if (*nplanes != 1 || sizes[0] < pix->sizeimage) - return -EINVAL; - count += vq->num_buffers; - } - - count = min_t(__u32, IMX7_CSI_VIDEO_MEM_LIMIT / pix->sizeimage, count); - - if (*nplanes) - *nbuffers = (count < vq->num_buffers) ? 0 : - count - vq->num_buffers; - else - *nbuffers = count; - - *nplanes = 1; - sizes[0] = pix->sizeimage; - - return 0; -} - -static int imx7_csi_video_buf_init(struct vb2_buffer *vb) -{ - struct imx7_csi_vb2_buffer *buf = to_imx7_csi_vb2_buffer(vb); - - INIT_LIST_HEAD(&buf->list); - - return 0; -} - -static int imx7_csi_video_buf_prepare(struct vb2_buffer *vb) -{ - struct imx7_csi *csi = vb2_get_drv_priv(vb->vb2_queue); - struct v4l2_pix_format *pix = &csi->vdev_fmt; - - if (vb2_plane_size(vb, 0) < pix->sizeimage) { - dev_err(csi->dev, - "data will not fit into plane (%lu < %lu)\n", - vb2_plane_size(vb, 0), (long)pix->sizeimage); - return -EINVAL; - } - - vb2_set_plane_payload(vb, 0, pix->sizeimage); - - return 0; -} - -static void imx7_csi_video_buf_queue(struct vb2_buffer *vb) -{ - struct imx7_csi *csi = vb2_get_drv_priv(vb->vb2_queue); - struct imx7_csi_vb2_buffer *buf = to_imx7_csi_vb2_buffer(vb); - unsigned long flags; - - spin_lock_irqsave(&csi->q_lock, flags); - - list_add_tail(&buf->list, &csi->ready_q); - - spin_unlock_irqrestore(&csi->q_lock, flags); -} - -static int imx7_csi_video_validate_fmt(struct imx7_csi *csi) -{ - struct v4l2_subdev_format fmt_src; - const struct imx7_csi_pixfmt *cc; - int ret; - - /* Retrieve the media bus format on the source subdev. */ - fmt_src.pad = IMX7_CSI_PAD_SRC; - fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(&csi->sd, pad, get_fmt, NULL, &fmt_src); - if (ret) - return ret; - - /* - * Verify that the media bus size matches the size set on the video - * node. It is sufficient to check the compose rectangle size without - * checking the rounded size from pix_fmt, as the rounded size is - * derived directly from the compose rectangle size, and will thus - * always match if the compose rectangle matches. - */ - if (csi->vdev_compose.width != fmt_src.format.width || - csi->vdev_compose.height != fmt_src.format.height) - return -EPIPE; - - /* - * Verify that the media bus code is compatible with the pixel format - * set on the video node. - */ - cc = imx7_csi_find_mbus_format(fmt_src.format.code); - if (!cc || csi->vdev_cc->yuv != cc->yuv) - return -EPIPE; - - return 0; -} - -static int imx7_csi_video_start_streaming(struct vb2_queue *vq, - unsigned int count) -{ - struct imx7_csi *csi = vb2_get_drv_priv(vq); - struct imx7_csi_vb2_buffer *buf, *tmp; - unsigned long flags; - int ret; - - ret = imx7_csi_video_validate_fmt(csi); - if (ret) { - dev_err(csi->dev, "capture format not valid\n"); - goto err_buffers; - } - - mutex_lock(&csi->mdev.graph_mutex); - - ret = __video_device_pipeline_start(csi->vdev, &csi->pipe); - if (ret) - goto err_unlock; - - ret = v4l2_subdev_call(&csi->sd, video, s_stream, 1); - if (ret) - goto err_stop; - - mutex_unlock(&csi->mdev.graph_mutex); - - return 0; - -err_stop: - __video_device_pipeline_stop(csi->vdev); -err_unlock: - mutex_unlock(&csi->mdev.graph_mutex); - dev_err(csi->dev, "pipeline start failed with %d\n", ret); -err_buffers: - spin_lock_irqsave(&csi->q_lock, flags); - list_for_each_entry_safe(buf, tmp, &csi->ready_q, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vbuf.vb2_buf, VB2_BUF_STATE_QUEUED); - } - spin_unlock_irqrestore(&csi->q_lock, flags); - return ret; -} - -static void imx7_csi_video_stop_streaming(struct vb2_queue *vq) -{ - struct imx7_csi *csi = vb2_get_drv_priv(vq); - struct imx7_csi_vb2_buffer *frame; - struct imx7_csi_vb2_buffer *tmp; - unsigned long flags; - - mutex_lock(&csi->mdev.graph_mutex); - v4l2_subdev_call(&csi->sd, video, s_stream, 0); - __video_device_pipeline_stop(csi->vdev); - mutex_unlock(&csi->mdev.graph_mutex); - - /* release all active buffers */ - spin_lock_irqsave(&csi->q_lock, flags); - list_for_each_entry_safe(frame, tmp, &csi->ready_q, list) { - list_del(&frame->list); - vb2_buffer_done(&frame->vbuf.vb2_buf, VB2_BUF_STATE_ERROR); - } - spin_unlock_irqrestore(&csi->q_lock, flags); -} - -static const struct vb2_ops imx7_csi_video_qops = { - .queue_setup = imx7_csi_video_queue_setup, - .buf_init = imx7_csi_video_buf_init, - .buf_prepare = imx7_csi_video_buf_prepare, - .buf_queue = imx7_csi_video_buf_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, - .start_streaming = imx7_csi_video_start_streaming, - .stop_streaming = imx7_csi_video_stop_streaming, -}; - -/* ----------------------------------------------------------------------------- - * Video Capture Device - File Operations - */ - -static int imx7_csi_video_open(struct file *file) -{ - struct imx7_csi *csi = video_drvdata(file); - int ret; - - if (mutex_lock_interruptible(&csi->vdev_mutex)) - return -ERESTARTSYS; - - ret = v4l2_fh_open(file); - if (ret) { - dev_err(csi->dev, "v4l2_fh_open failed\n"); - goto out; - } - - ret = v4l2_pipeline_pm_get(&csi->vdev->entity); - if (ret) - v4l2_fh_release(file); - -out: - mutex_unlock(&csi->vdev_mutex); - return ret; -} - -static int imx7_csi_video_release(struct file *file) -{ - struct imx7_csi *csi = video_drvdata(file); - struct vb2_queue *vq = &csi->q; - - mutex_lock(&csi->vdev_mutex); - - if (file->private_data == vq->owner) { - vb2_queue_release(vq); - vq->owner = NULL; - } - - v4l2_pipeline_pm_put(&csi->vdev->entity); - - v4l2_fh_release(file); - mutex_unlock(&csi->vdev_mutex); - return 0; -} - -static const struct v4l2_file_operations imx7_csi_video_fops = { - .owner = THIS_MODULE, - .open = imx7_csi_video_open, - .release = imx7_csi_video_release, - .poll = vb2_fop_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = vb2_fop_mmap, -}; - -/* ----------------------------------------------------------------------------- - * Video Capture Device - Init & Cleanup - */ - -static struct imx7_csi_vb2_buffer *imx7_csi_video_next_buf(struct imx7_csi *csi) -{ - struct imx7_csi_vb2_buffer *buf = NULL; - unsigned long flags; - - spin_lock_irqsave(&csi->q_lock, flags); - - /* get next queued buffer */ - if (!list_empty(&csi->ready_q)) { - buf = list_entry(csi->ready_q.next, struct imx7_csi_vb2_buffer, - list); - list_del(&buf->list); - } - - spin_unlock_irqrestore(&csi->q_lock, flags); - - return buf; -} - -static int imx7_csi_video_init_format(struct imx7_csi *csi) -{ - struct v4l2_subdev_format fmt_src = { - .pad = IMX7_CSI_PAD_SRC, - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - fmt_src.format.code = IMX7_CSI_DEF_MBUS_CODE; - fmt_src.format.width = IMX7_CSI_DEF_PIX_WIDTH; - fmt_src.format.height = IMX7_CSI_DEF_PIX_HEIGHT; - - imx7_csi_mbus_fmt_to_pix_fmt(&csi->vdev_fmt, &fmt_src.format, NULL); - csi->vdev_compose.width = fmt_src.format.width; - csi->vdev_compose.height = fmt_src.format.height; - - csi->vdev_cc = imx7_csi_find_pixel_format(csi->vdev_fmt.pixelformat); - - return 0; -} - -static int imx7_csi_video_register(struct imx7_csi *csi) -{ - struct v4l2_subdev *sd = &csi->sd; - struct v4l2_device *v4l2_dev = sd->v4l2_dev; - struct video_device *vdev = csi->vdev; - int ret; - - vdev->v4l2_dev = v4l2_dev; - - /* Initialize the default format and compose rectangle. */ - ret = imx7_csi_video_init_format(csi); - if (ret < 0) - return ret; - - /* Register the video device. */ - ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); - if (ret) { - dev_err(csi->dev, "Failed to register video device\n"); - return ret; - } - - dev_info(csi->dev, "Registered %s as /dev/%s\n", vdev->name, - video_device_node_name(vdev)); - - /* Create the link from the CSI subdev to the video device. */ - ret = media_create_pad_link(&sd->entity, IMX7_CSI_PAD_SRC, - &vdev->entity, 0, MEDIA_LNK_FL_IMMUTABLE | - MEDIA_LNK_FL_ENABLED); - if (ret) { - dev_err(csi->dev, "failed to create link to device node\n"); - video_unregister_device(vdev); - return ret; - } - - return 0; -} - -static void imx7_csi_video_unregister(struct imx7_csi *csi) -{ - media_entity_cleanup(&csi->vdev->entity); - video_unregister_device(csi->vdev); -} - -static int imx7_csi_video_init(struct imx7_csi *csi) -{ - struct video_device *vdev; - struct vb2_queue *vq; - int ret; - - mutex_init(&csi->vdev_mutex); - INIT_LIST_HEAD(&csi->ready_q); - spin_lock_init(&csi->q_lock); - - /* Allocate and initialize the video device. */ - vdev = video_device_alloc(); - if (!vdev) - return -ENOMEM; - - vdev->fops = &imx7_csi_video_fops; - vdev->ioctl_ops = &imx7_csi_video_ioctl_ops; - vdev->minor = -1; - vdev->release = video_device_release; - vdev->vfl_dir = VFL_DIR_RX; - vdev->tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; - vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING - | V4L2_CAP_IO_MC; - vdev->lock = &csi->vdev_mutex; - vdev->queue = &csi->q; - - snprintf(vdev->name, sizeof(vdev->name), "%s capture", csi->sd.name); - - video_set_drvdata(vdev, csi); - csi->vdev = vdev; - - /* Initialize the video device pad. */ - csi->vdev_pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_pads_init(&vdev->entity, 1, &csi->vdev_pad); - if (ret) { - video_device_release(vdev); - return ret; - } - - /* Initialize the vb2 queue. */ - vq = &csi->q; - vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - vq->io_modes = VB2_MMAP | VB2_DMABUF; - vq->drv_priv = csi; - vq->buf_struct_size = sizeof(struct imx7_csi_vb2_buffer); - vq->ops = &imx7_csi_video_qops; - vq->mem_ops = &vb2_dma_contig_memops; - vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - vq->lock = &csi->vdev_mutex; - vq->min_buffers_needed = 2; - vq->dev = csi->dev; - - ret = vb2_queue_init(vq); - if (ret) { - dev_err(csi->dev, "vb2_queue_init failed\n"); - video_device_release(vdev); - return ret; - } - - return 0; -} - -/* ----------------------------------------------------------------------------- - * V4L2 Subdev Operations - */ - -static int imx7_csi_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct imx7_csi *csi = v4l2_get_subdevdata(sd); - int ret = 0; - - mutex_lock(&csi->lock); - - if (!csi->src_sd) { - ret = -EPIPE; - goto out_unlock; - } - - if (csi->is_streaming == !!enable) - goto out_unlock; - - if (enable) { - ret = imx7_csi_init(csi); - if (ret < 0) - goto out_unlock; - - ret = v4l2_subdev_call(csi->src_sd, video, s_stream, 1); - if (ret < 0) { - imx7_csi_deinit(csi, VB2_BUF_STATE_QUEUED); - goto out_unlock; - } - - imx7_csi_enable(csi); - } else { - imx7_csi_disable(csi); - - v4l2_subdev_call(csi->src_sd, video, s_stream, 0); - - imx7_csi_deinit(csi, VB2_BUF_STATE_ERROR); - } - - csi->is_streaming = !!enable; - -out_unlock: - mutex_unlock(&csi->lock); - - return ret; -} - -static struct v4l2_mbus_framefmt * -imx7_csi_get_format(struct imx7_csi *csi, - struct v4l2_subdev_state *sd_state, - unsigned int pad, - enum v4l2_subdev_format_whence which) -{ - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(&csi->sd, sd_state, pad); - - return &csi->format_mbus[pad]; -} - -static int imx7_csi_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) -{ - const enum v4l2_subdev_format_whence which = - sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; - struct imx7_csi *csi = v4l2_get_subdevdata(sd); - const struct imx7_csi_pixfmt *cc; - int i; - - cc = imx7_csi_find_mbus_format(IMX7_CSI_DEF_MBUS_CODE); - - for (i = 0; i < IMX7_CSI_PADS_NUM; i++) { - struct v4l2_mbus_framefmt *mf = - imx7_csi_get_format(csi, sd_state, i, which); - - mf->code = IMX7_CSI_DEF_MBUS_CODE; - mf->width = IMX7_CSI_DEF_PIX_WIDTH; - mf->height = IMX7_CSI_DEF_PIX_HEIGHT; - mf->field = V4L2_FIELD_NONE; - - mf->colorspace = V4L2_COLORSPACE_SRGB; - mf->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(mf->colorspace); - mf->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(mf->colorspace); - mf->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(!cc->yuv, - mf->colorspace, mf->ycbcr_enc); - - csi->cc[i] = cc; - } - - return 0; -} - -static int imx7_csi_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct imx7_csi *csi = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *in_fmt; - int ret = 0; - - mutex_lock(&csi->lock); - - in_fmt = imx7_csi_get_format(csi, sd_state, IMX7_CSI_PAD_SINK, - code->which); - - switch (code->pad) { - case IMX7_CSI_PAD_SINK: - ret = imx7_csi_enum_mbus_formats(&code->code, code->index); - break; - case IMX7_CSI_PAD_SRC: - if (code->index != 0) { - ret = -EINVAL; - goto out_unlock; - } - - code->code = in_fmt->code; - break; - default: - ret = -EINVAL; - } - -out_unlock: - mutex_unlock(&csi->lock); - - return ret; -} - -static int imx7_csi_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *sdformat) -{ - struct imx7_csi *csi = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *fmt; - int ret = 0; - - mutex_lock(&csi->lock); - - fmt = imx7_csi_get_format(csi, sd_state, sdformat->pad, - sdformat->which); - if (!fmt) { - ret = -EINVAL; - goto out_unlock; - } - - sdformat->format = *fmt; - -out_unlock: - mutex_unlock(&csi->lock); - - return ret; -} - -/* - * Default the colorspace in tryfmt to SRGB if set to an unsupported - * colorspace or not initialized. Then set the remaining colorimetry - * parameters based on the colorspace if they are uninitialized. - * - * tryfmt->code must be set on entry. - */ -static void imx7_csi_try_colorimetry(struct v4l2_mbus_framefmt *tryfmt) -{ - const struct imx7_csi_pixfmt *cc; - bool is_rgb = false; - - cc = imx7_csi_find_mbus_format(tryfmt->code); - if (cc && !cc->yuv) - is_rgb = true; - - switch (tryfmt->colorspace) { - case V4L2_COLORSPACE_SMPTE170M: - case V4L2_COLORSPACE_REC709: - case V4L2_COLORSPACE_JPEG: - case V4L2_COLORSPACE_SRGB: - case V4L2_COLORSPACE_BT2020: - case V4L2_COLORSPACE_OPRGB: - case V4L2_COLORSPACE_DCI_P3: - case V4L2_COLORSPACE_RAW: - break; - default: - tryfmt->colorspace = V4L2_COLORSPACE_SRGB; - break; - } - - if (tryfmt->xfer_func == V4L2_XFER_FUNC_DEFAULT) - tryfmt->xfer_func = - V4L2_MAP_XFER_FUNC_DEFAULT(tryfmt->colorspace); - - if (tryfmt->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) - tryfmt->ycbcr_enc = - V4L2_MAP_YCBCR_ENC_DEFAULT(tryfmt->colorspace); - - if (tryfmt->quantization == V4L2_QUANTIZATION_DEFAULT) - tryfmt->quantization = - V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb, - tryfmt->colorspace, - tryfmt->ycbcr_enc); -} - -static int imx7_csi_try_fmt(struct imx7_csi *csi, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *sdformat, - const struct imx7_csi_pixfmt **cc) -{ - const struct imx7_csi_pixfmt *in_cc; - struct v4l2_mbus_framefmt *in_fmt; - u32 code; - - in_fmt = imx7_csi_get_format(csi, sd_state, IMX7_CSI_PAD_SINK, - sdformat->which); - if (!in_fmt) - return -EINVAL; - - switch (sdformat->pad) { - case IMX7_CSI_PAD_SRC: - in_cc = imx7_csi_find_mbus_format(in_fmt->code); - - sdformat->format.width = in_fmt->width; - sdformat->format.height = in_fmt->height; - sdformat->format.code = in_fmt->code; - sdformat->format.field = in_fmt->field; - *cc = in_cc; - - sdformat->format.colorspace = in_fmt->colorspace; - sdformat->format.xfer_func = in_fmt->xfer_func; - sdformat->format.quantization = in_fmt->quantization; - sdformat->format.ycbcr_enc = in_fmt->ycbcr_enc; - break; - case IMX7_CSI_PAD_SINK: - *cc = imx7_csi_find_mbus_format(sdformat->format.code); - if (!*cc) { - code = IMX7_CSI_DEF_MBUS_CODE; - *cc = imx7_csi_find_mbus_format(code); - sdformat->format.code = code; - } - - if (sdformat->format.field != V4L2_FIELD_INTERLACED) - sdformat->format.field = V4L2_FIELD_NONE; - break; - default: - return -EINVAL; - } - - imx7_csi_try_colorimetry(&sdformat->format); - - return 0; -} - -static int imx7_csi_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *sdformat) -{ - struct imx7_csi *csi = v4l2_get_subdevdata(sd); - const struct imx7_csi_pixfmt *outcc; - struct v4l2_mbus_framefmt *outfmt; - const struct imx7_csi_pixfmt *cc; - struct v4l2_mbus_framefmt *fmt; - struct v4l2_subdev_format format; - int ret = 0; - - if (sdformat->pad >= IMX7_CSI_PADS_NUM) - return -EINVAL; - - mutex_lock(&csi->lock); - - if (csi->is_streaming) { - ret = -EBUSY; - goto out_unlock; - } - - ret = imx7_csi_try_fmt(csi, sd_state, sdformat, &cc); - if (ret < 0) - goto out_unlock; - - fmt = imx7_csi_get_format(csi, sd_state, sdformat->pad, - sdformat->which); - if (!fmt) { - ret = -EINVAL; - goto out_unlock; - } - - *fmt = sdformat->format; - - if (sdformat->pad == IMX7_CSI_PAD_SINK) { - /* propagate format to source pads */ - format.pad = IMX7_CSI_PAD_SRC; - format.which = sdformat->which; - format.format = sdformat->format; - if (imx7_csi_try_fmt(csi, sd_state, &format, &outcc)) { - ret = -EINVAL; - goto out_unlock; - } - outfmt = imx7_csi_get_format(csi, sd_state, IMX7_CSI_PAD_SRC, - sdformat->which); - *outfmt = format.format; - - if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) - csi->cc[IMX7_CSI_PAD_SRC] = outcc; - } - - if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) - csi->cc[sdformat->pad] = cc; - -out_unlock: - mutex_unlock(&csi->lock); - - return ret; -} - -static int imx7_csi_pad_link_validate(struct v4l2_subdev *sd, - struct media_link *link, - struct v4l2_subdev_format *source_fmt, - struct v4l2_subdev_format *sink_fmt) -{ - struct imx7_csi *csi = v4l2_get_subdevdata(sd); - struct media_pad *pad = NULL; - unsigned int i; - int ret; - - if (!csi->src_sd) - return -EPIPE; - - /* - * Validate the source link, and record whether the source uses the - * parallel input or the CSI-2 receiver. - */ - ret = v4l2_subdev_link_validate_default(sd, link, source_fmt, sink_fmt); - if (ret) - return ret; - - switch (csi->src_sd->entity.function) { - case MEDIA_ENT_F_VID_IF_BRIDGE: - /* The input is the CSI-2 receiver. */ - csi->is_csi2 = true; - break; - - case MEDIA_ENT_F_VID_MUX: - /* The input is the mux, check its input. */ - for (i = 0; i < csi->src_sd->entity.num_pads; i++) { - struct media_pad *spad = &csi->src_sd->entity.pads[i]; - - if (!(spad->flags & MEDIA_PAD_FL_SINK)) - continue; - - pad = media_pad_remote_pad_first(spad); - if (pad) - break; - } - - if (!pad) - return -ENODEV; - - csi->is_csi2 = pad->entity->function == MEDIA_ENT_F_VID_IF_BRIDGE; - break; - - default: - /* - * The input is an external entity, it must use the parallel - * bus. - */ - csi->is_csi2 = false; - break; - } - - return 0; -} - -static int imx7_csi_registered(struct v4l2_subdev *sd) -{ - struct imx7_csi *csi = v4l2_get_subdevdata(sd); - int ret; - - ret = imx7_csi_video_init(csi); - if (ret) - return ret; - - ret = imx7_csi_video_register(csi); - if (ret) - return ret; - - ret = v4l2_device_register_subdev_nodes(&csi->v4l2_dev); - if (ret) - goto err_unreg; - - ret = media_device_register(&csi->mdev); - if (ret) - goto err_unreg; - - return 0; - -err_unreg: - imx7_csi_video_unregister(csi); - return ret; -} - -static void imx7_csi_unregistered(struct v4l2_subdev *sd) -{ - struct imx7_csi *csi = v4l2_get_subdevdata(sd); - - imx7_csi_video_unregister(csi); -} - -static const struct v4l2_subdev_video_ops imx7_csi_video_ops = { - .s_stream = imx7_csi_s_stream, -}; - -static const struct v4l2_subdev_pad_ops imx7_csi_pad_ops = { - .init_cfg = imx7_csi_init_cfg, - .enum_mbus_code = imx7_csi_enum_mbus_code, - .get_fmt = imx7_csi_get_fmt, - .set_fmt = imx7_csi_set_fmt, - .link_validate = imx7_csi_pad_link_validate, -}; - -static const struct v4l2_subdev_ops imx7_csi_subdev_ops = { - .video = &imx7_csi_video_ops, - .pad = &imx7_csi_pad_ops, -}; - -static const struct v4l2_subdev_internal_ops imx7_csi_internal_ops = { - .registered = imx7_csi_registered, - .unregistered = imx7_csi_unregistered, -}; - -/* ----------------------------------------------------------------------------- - * Media Entity Operations - */ - -static const struct media_entity_operations imx7_csi_entity_ops = { - .link_validate = v4l2_subdev_link_validate, - .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, -}; - -/* ----------------------------------------------------------------------------- - * Probe & Remove - */ - -static int imx7_csi_notify_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) -{ - struct imx7_csi *csi = imx7_csi_notifier_to_dev(notifier); - struct media_pad *sink = &csi->sd.entity.pads[IMX7_CSI_PAD_SINK]; - - csi->src_sd = sd; - - return v4l2_create_fwnode_links_to_pad(sd, sink, MEDIA_LNK_FL_ENABLED | - MEDIA_LNK_FL_IMMUTABLE); -} - -static int imx7_csi_notify_complete(struct v4l2_async_notifier *notifier) -{ - struct imx7_csi *csi = imx7_csi_notifier_to_dev(notifier); - - return v4l2_device_register_subdev_nodes(&csi->v4l2_dev); -} - -static const struct v4l2_async_notifier_operations imx7_csi_notify_ops = { - .bound = imx7_csi_notify_bound, - .complete = imx7_csi_notify_complete, -}; - -static int imx7_csi_async_register(struct imx7_csi *csi) -{ - struct v4l2_async_subdev *asd; - struct fwnode_handle *ep; - int ret; - - v4l2_async_nf_init(&csi->notifier); - - ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csi->dev), 0, 0, - FWNODE_GRAPH_ENDPOINT_NEXT); - if (ep) { - asd = v4l2_async_nf_add_fwnode_remote(&csi->notifier, ep, - struct v4l2_async_subdev); - - fwnode_handle_put(ep); - - if (IS_ERR(asd)) { - ret = PTR_ERR(asd); - /* OK if asd already exists */ - if (ret != -EEXIST) - return ret; - } - } - - csi->notifier.ops = &imx7_csi_notify_ops; - - ret = v4l2_async_nf_register(&csi->v4l2_dev, &csi->notifier); - if (ret) - return ret; - - return 0; -} - -static void imx7_csi_media_cleanup(struct imx7_csi *csi) -{ - v4l2_device_unregister(&csi->v4l2_dev); - media_device_unregister(&csi->mdev); - media_device_cleanup(&csi->mdev); -} - -static const struct media_device_ops imx7_csi_media_ops = { - .link_notify = v4l2_pipeline_link_notify, -}; - -static int imx7_csi_media_dev_init(struct imx7_csi *csi) -{ - int ret; - - strscpy(csi->mdev.model, "imx-media", sizeof(csi->mdev.model)); - csi->mdev.ops = &imx7_csi_media_ops; - csi->mdev.dev = csi->dev; - - csi->v4l2_dev.mdev = &csi->mdev; - strscpy(csi->v4l2_dev.name, "imx-media", - sizeof(csi->v4l2_dev.name)); - snprintf(csi->mdev.bus_info, sizeof(csi->mdev.bus_info), - "platform:%s", dev_name(csi->mdev.dev)); - - media_device_init(&csi->mdev); - - ret = v4l2_device_register(csi->dev, &csi->v4l2_dev); - if (ret < 0) { - v4l2_err(&csi->v4l2_dev, - "Failed to register v4l2_device: %d\n", ret); - goto cleanup; - } - - return 0; - -cleanup: - media_device_cleanup(&csi->mdev); - - return ret; -} - -static int imx7_csi_media_init(struct imx7_csi *csi) -{ - unsigned int i; - int ret; - - /* add media device */ - ret = imx7_csi_media_dev_init(csi); - if (ret) - return ret; - - v4l2_subdev_init(&csi->sd, &imx7_csi_subdev_ops); - v4l2_set_subdevdata(&csi->sd, csi); - csi->sd.internal_ops = &imx7_csi_internal_ops; - csi->sd.entity.ops = &imx7_csi_entity_ops; - csi->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; - csi->sd.dev = csi->dev; - csi->sd.owner = THIS_MODULE; - csi->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; - snprintf(csi->sd.name, sizeof(csi->sd.name), "csi"); - - for (i = 0; i < IMX7_CSI_PADS_NUM; i++) - csi->pad[i].flags = (i == IMX7_CSI_PAD_SINK) ? - MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; - - ret = media_entity_pads_init(&csi->sd.entity, IMX7_CSI_PADS_NUM, - csi->pad); - if (ret) - goto error; - - ret = v4l2_device_register_subdev(&csi->v4l2_dev, &csi->sd); - if (ret) - goto error; - - return 0; - -error: - imx7_csi_media_cleanup(csi); - return ret; -} - -static int imx7_csi_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct imx7_csi *csi; - int ret; - - csi = devm_kzalloc(&pdev->dev, sizeof(*csi), GFP_KERNEL); - if (!csi) - return -ENOMEM; - - csi->dev = dev; - platform_set_drvdata(pdev, csi); - - spin_lock_init(&csi->irqlock); - mutex_init(&csi->lock); - - /* Acquire resources and install interrupt handler. */ - csi->mclk = devm_clk_get(&pdev->dev, "mclk"); - if (IS_ERR(csi->mclk)) { - ret = PTR_ERR(csi->mclk); - dev_err(dev, "Failed to get mclk: %d", ret); - goto destroy_mutex; - } - - csi->irq = platform_get_irq(pdev, 0); - if (csi->irq < 0) { - ret = csi->irq; - goto destroy_mutex; - } - - csi->regbase = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(csi->regbase)) { - ret = PTR_ERR(csi->regbase); - goto destroy_mutex; - } - - csi->model = (enum imx_csi_model)(uintptr_t)of_device_get_match_data(&pdev->dev); - - ret = devm_request_irq(dev, csi->irq, imx7_csi_irq_handler, 0, "csi", - (void *)csi); - if (ret < 0) { - dev_err(dev, "Request CSI IRQ failed.\n"); - goto destroy_mutex; - } - - /* Initialize all the media device infrastructure. */ - ret = imx7_csi_media_init(csi); - if (ret) - goto destroy_mutex; - - /* Set the default mbus formats. */ - ret = imx7_csi_init_cfg(&csi->sd, NULL); - if (ret) - goto media_cleanup; - - ret = imx7_csi_async_register(csi); - if (ret) - goto subdev_notifier_cleanup; - - return 0; - -subdev_notifier_cleanup: - v4l2_async_nf_unregister(&csi->notifier); - v4l2_async_nf_cleanup(&csi->notifier); -media_cleanup: - imx7_csi_media_cleanup(csi); - -destroy_mutex: - mutex_destroy(&csi->lock); - - return ret; -} - -static int imx7_csi_remove(struct platform_device *pdev) -{ - struct imx7_csi *csi = platform_get_drvdata(pdev); - - imx7_csi_media_cleanup(csi); - - v4l2_async_nf_unregister(&csi->notifier); - v4l2_async_nf_cleanup(&csi->notifier); - v4l2_async_unregister_subdev(&csi->sd); - - mutex_destroy(&csi->lock); - - return 0; -} - -static const struct of_device_id imx7_csi_of_match[] = { - { .compatible = "fsl,imx8mq-csi", .data = (void *)IMX7_CSI_IMX8MQ }, - { .compatible = "fsl,imx7-csi", .data = (void *)IMX7_CSI_IMX7 }, - { .compatible = "fsl,imx6ul-csi", .data = (void *)IMX7_CSI_IMX7 }, - { }, -}; -MODULE_DEVICE_TABLE(of, imx7_csi_of_match); - -static struct platform_driver imx7_csi_driver = { - .probe = imx7_csi_probe, - .remove = imx7_csi_remove, - .driver = { - .of_match_table = imx7_csi_of_match, - .name = "imx7-csi", - }, -}; -module_platform_driver(imx7_csi_driver); - -MODULE_DESCRIPTION("i.MX7 CSI subdev driver"); -MODULE_AUTHOR("Rui Miguel Silva <rui.silva@linaro.org>"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:imx7-csi"); diff --git a/drivers/staging/media/ipu3/ipu3-v4l2.c b/drivers/staging/media/ipu3/ipu3-v4l2.c index ce13e746c15f..e530767e80a5 100644 --- a/drivers/staging/media/ipu3/ipu3-v4l2.c +++ b/drivers/staging/media/ipu3/ipu3-v4l2.c @@ -188,6 +188,28 @@ static int imgu_subdev_set_fmt(struct v4l2_subdev *sd, return 0; } +static struct v4l2_rect * +imgu_subdev_get_crop(struct imgu_v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_crop(&sd->subdev, sd_state, pad); + else + return &sd->rect.eff; +} + +static struct v4l2_rect * +imgu_subdev_get_compose(struct imgu_v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_compose(&sd->subdev, sd_state, pad); + else + return &sd->rect.bds; +} + static int imgu_subdev_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) @@ -200,18 +222,12 @@ static int imgu_subdev_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP: - if (sel->which == V4L2_SUBDEV_FORMAT_TRY) - sel->r = *v4l2_subdev_get_try_crop(sd, sd_state, - sel->pad); - else - sel->r = imgu_sd->rect.eff; + sel->r = *imgu_subdev_get_crop(imgu_sd, sd_state, sel->pad, + sel->which); return 0; case V4L2_SEL_TGT_COMPOSE: - if (sel->which == V4L2_SUBDEV_FORMAT_TRY) - sel->r = *v4l2_subdev_get_try_compose(sd, sd_state, - sel->pad); - else - sel->r = imgu_sd->rect.bds; + sel->r = *imgu_subdev_get_compose(imgu_sd, sd_state, sel->pad, + sel->which); return 0; default: return -EINVAL; @@ -223,10 +239,9 @@ static int imgu_subdev_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_selection *sel) { struct imgu_device *imgu = v4l2_get_subdevdata(sd); - struct imgu_v4l2_subdev *imgu_sd = container_of(sd, - struct imgu_v4l2_subdev, - subdev); - struct v4l2_rect *rect, *try_sel; + struct imgu_v4l2_subdev *imgu_sd = + container_of(sd, struct imgu_v4l2_subdev, subdev); + struct v4l2_rect *rect; dev_dbg(&imgu->pci_dev->dev, "set subdev %u sel which %u target 0x%4x rect [%ux%u]", @@ -238,22 +253,18 @@ static int imgu_subdev_set_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP: - try_sel = v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); - rect = &imgu_sd->rect.eff; + rect = imgu_subdev_get_crop(imgu_sd, sd_state, sel->pad, + sel->which); break; case V4L2_SEL_TGT_COMPOSE: - try_sel = v4l2_subdev_get_try_compose(sd, sd_state, sel->pad); - rect = &imgu_sd->rect.bds; + rect = imgu_subdev_get_compose(imgu_sd, sd_state, sel->pad, + sel->which); break; default: return -EINVAL; } - if (sel->which == V4L2_SUBDEV_FORMAT_TRY) - *try_sel = sel->r; - else - *rect = sel->r; - + *rect = sel->r; return 0; } diff --git a/drivers/staging/media/meson/vdec/codec_vp9.c b/drivers/staging/media/meson/vdec/codec_vp9.c index 897f5d7a6aad..394df5761556 100644 --- a/drivers/staging/media/meson/vdec/codec_vp9.c +++ b/drivers/staging/media/meson/vdec/codec_vp9.c @@ -1459,7 +1459,7 @@ static void vp9_tree_merge_probs(unsigned int *prev_prob, if (den == 0) { new_prob = pre_prob; } else { - m_count = den < MODE_MV_COUNT_SAT ? den : MODE_MV_COUNT_SAT; + m_count = min(den, MODE_MV_COUNT_SAT); get_prob = clip_prob(div_r32(((int64_t)tree_left * 256 + (den >> 1)), @@ -1513,7 +1513,7 @@ static void adapt_coef_probs_cxt(unsigned int *prev_prob, /* get binary prob */ num = branch_ct[node][0]; den = branch_ct[node][0] + branch_ct[node][1]; - m_count = den < count_sat ? den : count_sat; + m_count = min(den, count_sat); get_prob = (den == 0) ? 128u : @@ -1649,8 +1649,7 @@ static void adapt_coef_probs(int prev_kf, int cur_kf, int pre_fc, else if (coef_count_node_start == VP9_MV_BITS_1_COUNT_START) coef_node_start = VP9_MV_BITS_1_START; - else if (coef_count_node_start == - VP9_MV_CLASS0_HP_0_COUNT_START) + else /* node_start == VP9_MV_CLASS0_HP_0_COUNT_START */ coef_node_start = VP9_MV_CLASS0_HP_0_START; den = count[coef_count_node_start] + @@ -1664,8 +1663,7 @@ static void adapt_coef_probs(int prev_kf, int cur_kf, int pre_fc, if (den == 0) { new_prob = pre_prob; } else { - m_count = den < MODE_MV_COUNT_SAT ? - den : MODE_MV_COUNT_SAT; + m_count = min(den, MODE_MV_COUNT_SAT); get_prob = clip_prob(div_r32(((int64_t) count[coef_count_node_start] * 256 + diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 60f3d84be828..0ad70faa9ba0 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -19,8 +19,6 @@ #include <media/v4l2-ioctl.h> #include <media/v4l2-mc.h> -#include <asm/cacheflush.h> - #include "iss_video.h" #include "iss.h" diff --git a/drivers/staging/media/omap4iss/iss_video.h b/drivers/staging/media/omap4iss/iss_video.h index ca2d5edb6261..19668d28b682 100644 --- a/drivers/staging/media/omap4iss/iss_video.h +++ b/drivers/staging/media/omap4iss/iss_video.h @@ -53,19 +53,19 @@ enum iss_pipeline_stream_state { enum iss_pipeline_state { /* The stream has been started on the input video node. */ - ISS_PIPELINE_STREAM_INPUT = 1, + ISS_PIPELINE_STREAM_INPUT = BIT(0), /* The stream has been started on the output video node. */ - ISS_PIPELINE_STREAM_OUTPUT = (1 << 1), + ISS_PIPELINE_STREAM_OUTPUT = BIT(1), /* At least one buffer is queued on the input video node. */ - ISS_PIPELINE_QUEUE_INPUT = (1 << 2), + ISS_PIPELINE_QUEUE_INPUT = BIT(2), /* At least one buffer is queued on the output video node. */ - ISS_PIPELINE_QUEUE_OUTPUT = (1 << 3), + ISS_PIPELINE_QUEUE_OUTPUT = BIT(3), /* The input entity is idle, ready to be started. */ - ISS_PIPELINE_IDLE_INPUT = (1 << 4), + ISS_PIPELINE_IDLE_INPUT = BIT(4), /* The output entity is idle, ready to be started. */ - ISS_PIPELINE_IDLE_OUTPUT = (1 << 5), + ISS_PIPELINE_IDLE_OUTPUT = BIT(5), /* The pipeline is currently streaming. */ - ISS_PIPELINE_STREAM = (1 << 6), + ISS_PIPELINE_STREAM = BIT(6), }; /* @@ -126,9 +126,9 @@ struct iss_buffer { enum iss_video_dmaqueue_flags { /* Set if DMA queue becomes empty when ISS_PIPELINE_STREAM_CONTINUOUS */ - ISS_VIDEO_DMAQUEUE_UNDERRUN = (1 << 0), + ISS_VIDEO_DMAQUEUE_UNDERRUN = BIT(0), /* Set when queuing buffer to an empty DMA queue */ - ISS_VIDEO_DMAQUEUE_QUEUED = (1 << 1), + ISS_VIDEO_DMAQUEUE_QUEUED = BIT(1), }; #define iss_video_dmaqueue_flags_clr(video) \ diff --git a/drivers/staging/media/rkvdec/rkvdec-vp9.c b/drivers/staging/media/rkvdec/rkvdec-vp9.c index d8c1c0db15c7..cfae99b40ccb 100644 --- a/drivers/staging/media/rkvdec/rkvdec-vp9.c +++ b/drivers/staging/media/rkvdec/rkvdec-vp9.c @@ -84,6 +84,8 @@ struct rkvdec_vp9_probs { struct rkvdec_vp9_inter_frame_probs inter; struct rkvdec_vp9_intra_only_frame_probs intra_only; }; + /* 128 bit alignment */ + u8 padding1[11]; }; /* Data structure describing auxiliary buffer format. */ @@ -1006,6 +1008,7 @@ static int rkvdec_vp9_start(struct rkvdec_ctx *ctx) ctx->priv = vp9_ctx; + BUILD_BUG_ON(sizeof(priv_tbl->probs) % 16); /* ensure probs size is 128-bit aligned */ priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl), &vp9_ctx->priv_tbl.dma, GFP_KERNEL); if (!priv_tbl) { diff --git a/drivers/staging/media/sunxi/Kconfig b/drivers/staging/media/sunxi/Kconfig index 4549a135741f..62a486aba88b 100644 --- a/drivers/staging/media/sunxi/Kconfig +++ b/drivers/staging/media/sunxi/Kconfig @@ -12,5 +12,6 @@ config VIDEO_SUNXI if VIDEO_SUNXI source "drivers/staging/media/sunxi/cedrus/Kconfig" +source "drivers/staging/media/sunxi/sun6i-isp/Kconfig" endif diff --git a/drivers/staging/media/sunxi/Makefile b/drivers/staging/media/sunxi/Makefile index b87140b0e15f..3d20b2f0e644 100644 --- a/drivers/staging/media/sunxi/Makefile +++ b/drivers/staging/media/sunxi/Makefile @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_VIDEO_SUNXI_CEDRUS) += cedrus/ +obj-$(CONFIG_VIDEO_SUN6I_ISP) += sun6i-isp/ diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index 55c54dfdc585..a43d5ff66716 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -45,23 +45,37 @@ static int cedrus_try_ctrl(struct v4l2_ctrl *ctrl) } else if (ctrl->id == V4L2_CID_STATELESS_HEVC_SPS) { const struct v4l2_ctrl_hevc_sps *sps = ctrl->p_new.p_hevc_sps; struct cedrus_ctx *ctx = container_of(ctrl->handler, struct cedrus_ctx, hdl); + unsigned int bit_depth, max_depth; + struct vb2_queue *vq; if (sps->chroma_format_idc != 1) /* Only 4:2:0 is supported */ return -EINVAL; - if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) - /* Luma and chroma bit depth mismatch */ + bit_depth = max(sps->bit_depth_luma_minus8, + sps->bit_depth_chroma_minus8) + 8; + + if (cedrus_is_capable(ctx, CEDRUS_CAPABILITY_H265_10_DEC)) + max_depth = 10; + else + max_depth = 8; + + if (bit_depth > max_depth) return -EINVAL; - if (ctx->dev->capabilities & CEDRUS_CAPABILITY_H265_10_DEC) { - if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2) - /* Only 8-bit and 10-bit are supported */ + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + + /* + * Bit depth can't be higher than currently set once + * buffers are allocated. + */ + if (vb2_is_busy(vq)) { + if (ctx->bit_depth < bit_depth) return -EINVAL; } else { - if (sps->bit_depth_luma_minus8 != 0) - /* Only 8-bit is supported */ - return -EINVAL; + ctx->bit_depth = bit_depth; + cedrus_reset_cap_format(ctx); } } @@ -77,56 +91,56 @@ static const struct cedrus_control cedrus_controls[] = { .cfg = { .id = V4L2_CID_STATELESS_MPEG2_SEQUENCE, }, - .codec = CEDRUS_CODEC_MPEG2, + .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC, }, { .cfg = { .id = V4L2_CID_STATELESS_MPEG2_PICTURE, }, - .codec = CEDRUS_CODEC_MPEG2, + .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC, }, { .cfg = { .id = V4L2_CID_STATELESS_MPEG2_QUANTISATION, }, - .codec = CEDRUS_CODEC_MPEG2, + .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC, }, { .cfg = { .id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, }, - .codec = CEDRUS_CODEC_H264, + .capabilities = CEDRUS_CAPABILITY_H264_DEC, }, { .cfg = { .id = V4L2_CID_STATELESS_H264_SLICE_PARAMS, }, - .codec = CEDRUS_CODEC_H264, + .capabilities = CEDRUS_CAPABILITY_H264_DEC, }, { .cfg = { .id = V4L2_CID_STATELESS_H264_SPS, .ops = &cedrus_ctrl_ops, }, - .codec = CEDRUS_CODEC_H264, + .capabilities = CEDRUS_CAPABILITY_H264_DEC, }, { .cfg = { .id = V4L2_CID_STATELESS_H264_PPS, }, - .codec = CEDRUS_CODEC_H264, + .capabilities = CEDRUS_CAPABILITY_H264_DEC, }, { .cfg = { .id = V4L2_CID_STATELESS_H264_SCALING_MATRIX, }, - .codec = CEDRUS_CODEC_H264, + .capabilities = CEDRUS_CAPABILITY_H264_DEC, }, { .cfg = { .id = V4L2_CID_STATELESS_H264_PRED_WEIGHTS, }, - .codec = CEDRUS_CODEC_H264, + .capabilities = CEDRUS_CAPABILITY_H264_DEC, }, { .cfg = { @@ -134,7 +148,7 @@ static const struct cedrus_control cedrus_controls[] = { .max = V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED, .def = V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED, }, - .codec = CEDRUS_CODEC_H264, + .capabilities = CEDRUS_CAPABILITY_H264_DEC, }, { .cfg = { @@ -142,7 +156,7 @@ static const struct cedrus_control cedrus_controls[] = { .max = V4L2_STATELESS_H264_START_CODE_NONE, .def = V4L2_STATELESS_H264_START_CODE_NONE, }, - .codec = CEDRUS_CODEC_H264, + .capabilities = CEDRUS_CAPABILITY_H264_DEC, }, /* * We only expose supported profiles information, @@ -160,20 +174,20 @@ static const struct cedrus_control cedrus_controls[] = { .menu_skip_mask = BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED), }, - .codec = CEDRUS_CODEC_H264, + .capabilities = CEDRUS_CAPABILITY_H264_DEC, }, { .cfg = { .id = V4L2_CID_STATELESS_HEVC_SPS, .ops = &cedrus_ctrl_ops, }, - .codec = CEDRUS_CODEC_H265, + .capabilities = CEDRUS_CAPABILITY_H265_DEC, }, { .cfg = { .id = V4L2_CID_STATELESS_HEVC_PPS, }, - .codec = CEDRUS_CODEC_H265, + .capabilities = CEDRUS_CAPABILITY_H265_DEC, }, { .cfg = { @@ -181,13 +195,13 @@ static const struct cedrus_control cedrus_controls[] = { /* The driver can only handle 1 entry per slice for now */ .dims = { 1 }, }, - .codec = CEDRUS_CODEC_H265, + .capabilities = CEDRUS_CAPABILITY_H265_DEC, }, { .cfg = { .id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX, }, - .codec = CEDRUS_CODEC_H265, + .capabilities = CEDRUS_CAPABILITY_H265_DEC, }, { .cfg = { @@ -197,7 +211,7 @@ static const struct cedrus_control cedrus_controls[] = { .max = 0xffffffff, .step = 1, }, - .codec = CEDRUS_CODEC_H265, + .capabilities = CEDRUS_CAPABILITY_H265_DEC, }, { .cfg = { @@ -205,7 +219,7 @@ static const struct cedrus_control cedrus_controls[] = { .max = V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED, .def = V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED, }, - .codec = CEDRUS_CODEC_H265, + .capabilities = CEDRUS_CAPABILITY_H265_DEC, }, { .cfg = { @@ -213,19 +227,19 @@ static const struct cedrus_control cedrus_controls[] = { .max = V4L2_STATELESS_HEVC_START_CODE_NONE, .def = V4L2_STATELESS_HEVC_START_CODE_NONE, }, - .codec = CEDRUS_CODEC_H265, + .capabilities = CEDRUS_CAPABILITY_H265_DEC, }, { .cfg = { .id = V4L2_CID_STATELESS_VP8_FRAME, }, - .codec = CEDRUS_CODEC_VP8, + .capabilities = CEDRUS_CAPABILITY_VP8_DEC, }, { .cfg = { .id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS, }, - .codec = CEDRUS_CODEC_H265, + .capabilities = CEDRUS_CAPABILITY_H265_DEC, }, }; @@ -258,7 +272,7 @@ static int cedrus_init_ctrls(struct cedrus_dev *dev, struct cedrus_ctx *ctx) struct v4l2_ctrl_handler *hdl = &ctx->hdl; struct v4l2_ctrl *ctrl; unsigned int ctrl_size; - unsigned int i; + unsigned int i, j; v4l2_ctrl_handler_init(hdl, CEDRUS_CONTROLS_COUNT); if (hdl->error) { @@ -274,7 +288,11 @@ static int cedrus_init_ctrls(struct cedrus_dev *dev, struct cedrus_ctx *ctx) if (!ctx->ctrls) return -ENOMEM; + j = 0; for (i = 0; i < CEDRUS_CONTROLS_COUNT; i++) { + if (!cedrus_is_capable(ctx, cedrus_controls[i].capabilities)) + continue; + ctrl = v4l2_ctrl_new_custom(hdl, &cedrus_controls[i].cfg, NULL); if (hdl->error) { @@ -289,7 +307,7 @@ static int cedrus_init_ctrls(struct cedrus_dev *dev, struct cedrus_ctx *ctx) return hdl->error; } - ctx->ctrls[i] = ctrl; + ctx->ctrls[j++] = ctrl; } ctx->fh.ctrl_handler = hdl; @@ -350,27 +368,20 @@ static int cedrus_open(struct file *file) v4l2_fh_init(&ctx->fh, video_devdata(file)); file->private_data = &ctx->fh; ctx->dev = dev; - - ret = cedrus_init_ctrls(dev, ctx); - if (ret) - goto err_free; + ctx->bit_depth = 8; ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &cedrus_queue_init); if (IS_ERR(ctx->fh.m2m_ctx)) { ret = PTR_ERR(ctx->fh.m2m_ctx); - goto err_ctrls; + goto err_free; } - ctx->dst_fmt.pixelformat = V4L2_PIX_FMT_NV12_32L32; - cedrus_prepare_format(&ctx->dst_fmt); - ctx->src_fmt.pixelformat = V4L2_PIX_FMT_MPEG2_SLICE; - /* - * TILED_NV12 has more strict requirements, so copy the width and - * height to src_fmt to ensure that is matches the dst_fmt resolution. - */ - ctx->src_fmt.width = ctx->dst_fmt.width; - ctx->src_fmt.height = ctx->dst_fmt.height; - cedrus_prepare_format(&ctx->src_fmt); + + cedrus_reset_out_format(ctx); + + ret = cedrus_init_ctrls(dev, ctx); + if (ret) + goto err_m2m_release; v4l2_fh_add(&ctx->fh); @@ -378,8 +389,8 @@ static int cedrus_open(struct file *file) return 0; -err_ctrls: - v4l2_ctrl_handler_free(&ctx->hdl); +err_m2m_release: + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); err_free: kfree(ctx); mutex_unlock(&dev->dev_mutex); @@ -460,11 +471,6 @@ static int cedrus_probe(struct platform_device *pdev) return ret; } - dev->dec_ops[CEDRUS_CODEC_MPEG2] = &cedrus_dec_ops_mpeg2; - dev->dec_ops[CEDRUS_CODEC_H264] = &cedrus_dec_ops_h264; - dev->dec_ops[CEDRUS_CODEC_H265] = &cedrus_dec_ops_h265; - dev->dec_ops[CEDRUS_CODEC_VP8] = &cedrus_dec_ops_vp8; - mutex_init(&dev->dev_mutex); INIT_DELAYED_WORK(&dev->watchdog_work, cedrus_watchdog); diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h index 93a2196006f7..522c184e2afc 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus.h @@ -35,14 +35,6 @@ #define CEDRUS_CAPABILITY_VP8_DEC BIT(4) #define CEDRUS_CAPABILITY_H265_10_DEC BIT(5) -enum cedrus_codec { - CEDRUS_CODEC_MPEG2, - CEDRUS_CODEC_H264, - CEDRUS_CODEC_H265, - CEDRUS_CODEC_VP8, - CEDRUS_CODEC_LAST, -}; - enum cedrus_irq_status { CEDRUS_IRQ_NONE, CEDRUS_IRQ_ERROR, @@ -57,7 +49,7 @@ enum cedrus_h264_pic_type { struct cedrus_control { struct v4l2_ctrl_config cfg; - enum cedrus_codec codec; + unsigned int capabilities; }; struct cedrus_h264_run { @@ -108,7 +100,15 @@ struct cedrus_buffer { struct { unsigned int position; enum cedrus_h264_pic_type pic_type; + void *mv_col_buf; + dma_addr_t mv_col_buf_dma; + ssize_t mv_col_buf_size; } h264; + struct { + void *mv_col_buf; + dma_addr_t mv_col_buf_dma; + ssize_t mv_col_buf_size; + } h265; } codec; }; @@ -118,17 +118,14 @@ struct cedrus_ctx { struct v4l2_pix_format src_fmt; struct v4l2_pix_format dst_fmt; - enum cedrus_codec current_codec; + struct cedrus_dec_ops *current_codec; + unsigned int bit_depth; struct v4l2_ctrl_handler hdl; struct v4l2_ctrl **ctrls; union { struct { - void *mv_col_buf; - dma_addr_t mv_col_buf_dma; - ssize_t mv_col_buf_field_size; - ssize_t mv_col_buf_size; void *pic_info_buf; dma_addr_t pic_info_buf_dma; ssize_t pic_info_buf_size; @@ -142,10 +139,6 @@ struct cedrus_ctx { ssize_t intra_pred_buf_size; } h264; struct { - void *mv_col_buf; - dma_addr_t mv_col_buf_addr; - ssize_t mv_col_buf_size; - ssize_t mv_col_buf_unit_size; void *neighbor_info_buf; dma_addr_t neighbor_info_buf_addr; void *entry_points_buf; @@ -170,6 +163,8 @@ struct cedrus_dec_ops { int (*start)(struct cedrus_ctx *ctx); void (*stop)(struct cedrus_ctx *ctx); void (*trigger)(struct cedrus_ctx *ctx); + unsigned int (*extra_cap_size)(struct cedrus_ctx *ctx, + struct v4l2_pix_format *pix_fmt); }; struct cedrus_variant { @@ -185,7 +180,6 @@ struct cedrus_dev { struct platform_device *pdev; struct device *dev; struct v4l2_m2m_dev *m2m_dev; - struct cedrus_dec_ops *dec_ops[CEDRUS_CODEC_LAST]; /* Device file mutex */ struct mutex dev_mutex; @@ -268,6 +262,12 @@ vb2_to_cedrus_buffer(const struct vb2_buffer *p) return vb2_v4l2_to_cedrus_buffer(to_vb2_v4l2_buffer(p)); } +static inline bool +cedrus_is_capable(struct cedrus_ctx *ctx, unsigned int capabilities) +{ + return (ctx->dev->capabilities & capabilities) == capabilities; +} + void *cedrus_find_control_data(struct cedrus_ctx *ctx, u32 id); u32 cedrus_get_num_of_controls(struct cedrus_ctx *ctx, u32 id); diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c index e7f7602a5ab4..fbbf9e6f0f50 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c @@ -94,7 +94,7 @@ void cedrus_device_run(void *priv) cedrus_dst_format_set(dev, &ctx->dst_fmt); - error = dev->dec_ops[ctx->current_codec]->setup(ctx, &run); + error = ctx->current_codec->setup(ctx, &run); if (error) v4l2_err(&ctx->dev->v4l2_dev, "Failed to setup decoding job: %d\n", error); @@ -110,7 +110,7 @@ void cedrus_device_run(void *priv) schedule_delayed_work(&dev->watchdog_work, msecs_to_jiffies(2000)); - dev->dec_ops[ctx->current_codec]->trigger(ctx); + ctx->current_codec->trigger(ctx); } else { v4l2_m2m_buf_done_and_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx, diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c index a8b236cd3800..dfb401df138a 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c @@ -54,17 +54,13 @@ static void cedrus_h264_write_sram(struct cedrus_dev *dev, cedrus_write(dev, VE_AVC_SRAM_PORT_DATA, *buffer++); } -static dma_addr_t cedrus_h264_mv_col_buf_addr(struct cedrus_ctx *ctx, - unsigned int position, +static dma_addr_t cedrus_h264_mv_col_buf_addr(struct cedrus_buffer *buf, unsigned int field) { - dma_addr_t addr = ctx->codec.h264.mv_col_buf_dma; - - /* Adjust for the position */ - addr += position * ctx->codec.h264.mv_col_buf_field_size * 2; + dma_addr_t addr = buf->codec.h264.mv_col_buf_dma; /* Adjust for the field */ - addr += field * ctx->codec.h264.mv_col_buf_field_size; + addr += field * buf->codec.h264.mv_col_buf_size / 2; return addr; } @@ -76,7 +72,6 @@ static void cedrus_fill_ref_pic(struct cedrus_ctx *ctx, struct cedrus_h264_sram_ref_pic *pic) { struct vb2_buffer *vbuf = &buf->m2m_buf.vb.vb2_buf; - unsigned int position = buf->codec.h264.position; pic->top_field_order_cnt = cpu_to_le32(top_field_order_cnt); pic->bottom_field_order_cnt = cpu_to_le32(bottom_field_order_cnt); @@ -84,14 +79,12 @@ static void cedrus_fill_ref_pic(struct cedrus_ctx *ctx, pic->luma_ptr = cpu_to_le32(cedrus_buf_addr(vbuf, &ctx->dst_fmt, 0)); pic->chroma_ptr = cpu_to_le32(cedrus_buf_addr(vbuf, &ctx->dst_fmt, 1)); - pic->mv_col_top_ptr = - cpu_to_le32(cedrus_h264_mv_col_buf_addr(ctx, position, 0)); - pic->mv_col_bot_ptr = - cpu_to_le32(cedrus_h264_mv_col_buf_addr(ctx, position, 1)); + pic->mv_col_top_ptr = cpu_to_le32(cedrus_h264_mv_col_buf_addr(buf, 0)); + pic->mv_col_bot_ptr = cpu_to_le32(cedrus_h264_mv_col_buf_addr(buf, 1)); } -static void cedrus_write_frame_list(struct cedrus_ctx *ctx, - struct cedrus_run *run) +static int cedrus_write_frame_list(struct cedrus_ctx *ctx, + struct cedrus_run *run) { struct cedrus_h264_sram_ref_pic pic_list[CEDRUS_H264_FRAME_NUM]; const struct v4l2_ctrl_h264_decode_params *decode = run->h264.decode_params; @@ -146,6 +139,31 @@ static void cedrus_write_frame_list(struct cedrus_ctx *ctx, output_buf = vb2_to_cedrus_buffer(&run->dst->vb2_buf); output_buf->codec.h264.position = position; + if (!output_buf->codec.h264.mv_col_buf_size) { + const struct v4l2_ctrl_h264_sps *sps = run->h264.sps; + unsigned int field_size; + + field_size = DIV_ROUND_UP(ctx->src_fmt.width, 16) * + DIV_ROUND_UP(ctx->src_fmt.height, 16) * 16; + if (!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE)) + field_size = field_size * 2; + if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)) + field_size = field_size * 2; + + output_buf->codec.h264.mv_col_buf_size = field_size * 2; + /* Buffer is never accessed by CPU, so we can skip kernel mapping. */ + output_buf->codec.h264.mv_col_buf = + dma_alloc_attrs(dev->dev, + output_buf->codec.h264.mv_col_buf_size, + &output_buf->codec.h264.mv_col_buf_dma, + GFP_KERNEL, DMA_ATTR_NO_KERNEL_MAPPING); + + if (!output_buf->codec.h264.mv_col_buf) { + output_buf->codec.h264.mv_col_buf_size = 0; + return -ENOMEM; + } + } + if (decode->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC) output_buf->codec.h264.pic_type = CEDRUS_H264_PIC_TYPE_FIELD; else if (sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD) @@ -162,6 +180,8 @@ static void cedrus_write_frame_list(struct cedrus_ctx *ctx, pic_list, sizeof(pic_list)); cedrus_write(dev, VE_H264_OUTPUT_FRAME_IDX, position); + + return 0; } #define CEDRUS_MAX_REF_IDX 32 @@ -496,8 +516,9 @@ static void cedrus_h264_irq_disable(struct cedrus_ctx *ctx) static int cedrus_h264_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) { struct cedrus_dev *dev = ctx->dev; + int ret; - cedrus_engine_enable(ctx, CEDRUS_CODEC_H264); + cedrus_engine_enable(ctx); cedrus_write(dev, VE_H264_SDROT_CTRL, 0); cedrus_write(dev, VE_H264_EXTRA_BUFFER1, @@ -506,7 +527,9 @@ static int cedrus_h264_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) ctx->codec.h264.neighbor_info_buf_dma); cedrus_write_scaling_lists(ctx, run); - cedrus_write_frame_list(ctx, run); + ret = cedrus_write_frame_list(ctx, run); + if (ret) + return ret; cedrus_set_params(ctx, run); @@ -517,8 +540,6 @@ static int cedrus_h264_start(struct cedrus_ctx *ctx) { struct cedrus_dev *dev = ctx->dev; unsigned int pic_info_size; - unsigned int field_size; - unsigned int mv_col_size; int ret; /* @@ -566,38 +587,6 @@ static int cedrus_h264_start(struct cedrus_ctx *ctx) goto err_pic_buf; } - field_size = DIV_ROUND_UP(ctx->src_fmt.width, 16) * - DIV_ROUND_UP(ctx->src_fmt.height, 16) * 16; - - /* - * FIXME: This is actually conditional to - * V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE not being set, we - * might have to rework this if memory efficiency ever is - * something we need to work on. - */ - field_size = field_size * 2; - - /* - * FIXME: This is actually conditional to - * V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY not being set, we might - * have to rework this if memory efficiency ever is something - * we need to work on. - */ - field_size = field_size * 2; - ctx->codec.h264.mv_col_buf_field_size = field_size; - - mv_col_size = field_size * 2 * CEDRUS_H264_FRAME_NUM; - ctx->codec.h264.mv_col_buf_size = mv_col_size; - ctx->codec.h264.mv_col_buf = - dma_alloc_attrs(dev->dev, - ctx->codec.h264.mv_col_buf_size, - &ctx->codec.h264.mv_col_buf_dma, - GFP_KERNEL, DMA_ATTR_NO_KERNEL_MAPPING); - if (!ctx->codec.h264.mv_col_buf) { - ret = -ENOMEM; - goto err_neighbor_buf; - } - if (ctx->src_fmt.width > 2048) { /* * Formulas for deblock and intra prediction buffer sizes @@ -613,7 +602,7 @@ static int cedrus_h264_start(struct cedrus_ctx *ctx) GFP_KERNEL, DMA_ATTR_NO_KERNEL_MAPPING); if (!ctx->codec.h264.deblk_buf) { ret = -ENOMEM; - goto err_mv_col_buf; + goto err_neighbor_buf; } /* @@ -641,12 +630,6 @@ err_deblk_buf: ctx->codec.h264.deblk_buf_dma, DMA_ATTR_NO_KERNEL_MAPPING); -err_mv_col_buf: - dma_free_attrs(dev->dev, ctx->codec.h264.mv_col_buf_size, - ctx->codec.h264.mv_col_buf, - ctx->codec.h264.mv_col_buf_dma, - DMA_ATTR_NO_KERNEL_MAPPING); - err_neighbor_buf: dma_free_attrs(dev->dev, CEDRUS_NEIGHBOR_INFO_BUF_SIZE, ctx->codec.h264.neighbor_info_buf, @@ -664,11 +647,26 @@ err_pic_buf: static void cedrus_h264_stop(struct cedrus_ctx *ctx) { struct cedrus_dev *dev = ctx->dev; + struct cedrus_buffer *buf; + struct vb2_queue *vq; + unsigned int i; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + + for (i = 0; i < vq->num_buffers; i++) { + buf = vb2_to_cedrus_buffer(vb2_get_buffer(vq, i)); + + if (buf->codec.h264.mv_col_buf_size > 0) { + dma_free_attrs(dev->dev, + buf->codec.h264.mv_col_buf_size, + buf->codec.h264.mv_col_buf, + buf->codec.h264.mv_col_buf_dma, + DMA_ATTR_NO_KERNEL_MAPPING); + + buf->codec.h264.mv_col_buf_size = 0; + } + } - dma_free_attrs(dev->dev, ctx->codec.h264.mv_col_buf_size, - ctx->codec.h264.mv_col_buf, - ctx->codec.h264.mv_col_buf_dma, - DMA_ATTR_NO_KERNEL_MAPPING); dma_free_attrs(dev->dev, CEDRUS_NEIGHBOR_INFO_BUF_SIZE, ctx->codec.h264.neighbor_info_buf, ctx->codec.h264.neighbor_info_buf_dma, diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c index 4952fc17f3e6..fc9297232456 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c @@ -41,6 +41,19 @@ struct cedrus_h265_sram_pred_weight { __s8 offset; } __packed; +static unsigned int cedrus_h265_2bit_size(unsigned int width, + unsigned int height) +{ + /* + * Vendor library additionally aligns width and height to 16, + * but all capture formats are already aligned to that anyway, + * so we can skip that here. All formats are also one form of + * YUV 4:2:0 or another, so we can safely assume multiplication + * factor of 1.5. + */ + return ALIGN(width / 4, 32) * height * 3 / 2; +} + static enum cedrus_irq_status cedrus_h265_irq_status(struct cedrus_ctx *ctx) { struct cedrus_dev *dev = ctx->dev; @@ -90,12 +103,13 @@ static void cedrus_h265_sram_write_data(struct cedrus_dev *dev, void *data, } static inline dma_addr_t -cedrus_h265_frame_info_mv_col_buf_addr(struct cedrus_ctx *ctx, - unsigned int index, unsigned int field) +cedrus_h265_frame_info_mv_col_buf_addr(struct vb2_buffer *buf, + unsigned int field) { - return ctx->codec.h265.mv_col_buf_addr + index * - ctx->codec.h265.mv_col_buf_unit_size + - field * ctx->codec.h265.mv_col_buf_unit_size / 2; + struct cedrus_buffer *cedrus_buf = vb2_to_cedrus_buffer(buf); + + return cedrus_buf->codec.h265.mv_col_buf_dma + + field * cedrus_buf->codec.h265.mv_col_buf_size / 2; } static void cedrus_h265_frame_info_write_single(struct cedrus_ctx *ctx, @@ -108,9 +122,8 @@ static void cedrus_h265_frame_info_write_single(struct cedrus_ctx *ctx, dma_addr_t dst_luma_addr = cedrus_dst_buf_addr(ctx, buf, 0); dma_addr_t dst_chroma_addr = cedrus_dst_buf_addr(ctx, buf, 1); dma_addr_t mv_col_buf_addr[2] = { - cedrus_h265_frame_info_mv_col_buf_addr(ctx, buf->index, 0), - cedrus_h265_frame_info_mv_col_buf_addr(ctx, buf->index, - field_pic ? 1 : 0) + cedrus_h265_frame_info_mv_col_buf_addr(buf, 0), + cedrus_h265_frame_info_mv_col_buf_addr(buf, field_pic ? 1 : 0) }; u32 offset = VE_DEC_H265_SRAM_OFFSET_FRAME_INFO + VE_DEC_H265_SRAM_OFFSET_FRAME_INFO_UNIT * index; @@ -242,6 +255,18 @@ static void cedrus_h265_skip_bits(struct cedrus_dev *dev, int num) } } +static u32 cedrus_h265_show_bits(struct cedrus_dev *dev, int num) +{ + cedrus_write(dev, VE_DEC_H265_TRIGGER, + VE_DEC_H265_TRIGGER_SHOW_BITS | + VE_DEC_H265_TRIGGER_TYPE_N_BITS(num)); + + cedrus_wait_for(dev, VE_DEC_H265_STATUS, + VE_DEC_H265_STATUS_VLD_BUSY); + + return cedrus_read(dev, VE_DEC_H265_BITS_READ); +} + static void cedrus_h265_write_scaling_list(struct cedrus_ctx *ctx, struct cedrus_run *run) { @@ -400,13 +425,14 @@ static int cedrus_h265_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) unsigned int width_in_ctb_luma, ctb_size_luma; unsigned int log2_max_luma_coding_block_size; unsigned int ctb_addr_x, ctb_addr_y; + struct cedrus_buffer *cedrus_buf; dma_addr_t src_buf_addr; dma_addr_t src_buf_end_addr; u32 chroma_log2_weight_denom; u32 num_entry_point_offsets; u32 output_pic_list_index; u32 pic_order_cnt[2]; - u8 *padding; + u8 padding; int count; u32 reg; @@ -416,6 +442,7 @@ static int cedrus_h265_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) decode_params = run->h265.decode_params; pred_weight_table = &slice_params->pred_weight_table; num_entry_point_offsets = slice_params->num_entry_point_offsets; + cedrus_buf = vb2_to_cedrus_buffer(&run->dst->vb2_buf); /* * If entry points offsets are present, we should get them @@ -433,37 +460,31 @@ static int cedrus_h265_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) DIV_ROUND_UP(sps->pic_width_in_luma_samples, ctb_size_luma); /* MV column buffer size and allocation. */ - if (!ctx->codec.h265.mv_col_buf_size) { - unsigned int num_buffers = - run->dst->vb2_buf.vb2_queue->num_buffers; - + if (!cedrus_buf->codec.h265.mv_col_buf_size) { /* * Each CTB requires a MV col buffer with a specific unit size. * Since the address is given with missing lsb bits, 1 KiB is * added to each buffer to ensure proper alignment. */ - ctx->codec.h265.mv_col_buf_unit_size = + cedrus_buf->codec.h265.mv_col_buf_size = DIV_ROUND_UP(ctx->src_fmt.width, ctb_size_luma) * DIV_ROUND_UP(ctx->src_fmt.height, ctb_size_luma) * CEDRUS_H265_MV_COL_BUF_UNIT_CTB_SIZE + SZ_1K; - ctx->codec.h265.mv_col_buf_size = num_buffers * - ctx->codec.h265.mv_col_buf_unit_size; - /* Buffer is never accessed by CPU, so we can skip kernel mapping. */ - ctx->codec.h265.mv_col_buf = + cedrus_buf->codec.h265.mv_col_buf = dma_alloc_attrs(dev->dev, - ctx->codec.h265.mv_col_buf_size, - &ctx->codec.h265.mv_col_buf_addr, + cedrus_buf->codec.h265.mv_col_buf_size, + &cedrus_buf->codec.h265.mv_col_buf_dma, GFP_KERNEL, DMA_ATTR_NO_KERNEL_MAPPING); - if (!ctx->codec.h265.mv_col_buf) { - ctx->codec.h265.mv_col_buf_size = 0; + if (!cedrus_buf->codec.h265.mv_col_buf) { + cedrus_buf->codec.h265.mv_col_buf_size = 0; return -ENOMEM; } } /* Activate H265 engine. */ - cedrus_engine_enable(ctx, CEDRUS_CODEC_H265); + cedrus_engine_enable(ctx); /* Source offset and length in bits. */ @@ -520,21 +541,22 @@ static int cedrus_h265_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) if (slice_params->data_byte_offset == 0) return -EOPNOTSUPP; - padding = (u8 *)vb2_plane_vaddr(&run->src->vb2_buf, 0) + - slice_params->data_byte_offset - 1; + cedrus_h265_skip_bits(dev, (slice_params->data_byte_offset - 1) * 8); + + padding = cedrus_h265_show_bits(dev, 8); /* at least one bit must be set in that byte */ - if (*padding == 0) + if (padding == 0) return -EINVAL; for (count = 0; count < 8; count++) - if (*padding & (1 << count)) + if (padding & (1 << count)) break; /* Include the one bit. */ count++; - cedrus_h265_skip_bits(dev, slice_params->data_byte_offset * 8 - count); + cedrus_h265_skip_bits(dev, 8 - count); /* Bitstream parameters. */ @@ -793,6 +815,18 @@ static int cedrus_h265_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) VE_DEC_H265_SRAM_OFFSET_PRED_WEIGHT_CHROMA_L1); } + if (ctx->bit_depth > 8) { + unsigned int stride = ALIGN(ctx->dst_fmt.width / 4, 32); + + reg = ctx->dst_fmt.sizeimage - + cedrus_h265_2bit_size(ctx->dst_fmt.width, + ctx->dst_fmt.height); + cedrus_write(dev, VE_DEC_H265_OFFSET_ADDR_FIRST_OUT, reg); + + reg = VE_DEC_H265_10BIT_CONFIGURE_FIRST_2BIT_STRIDE(stride); + cedrus_write(dev, VE_DEC_H265_10BIT_CONFIGURE, reg); + } + /* Enable appropriate interruptions. */ cedrus_write(dev, VE_DEC_H265_CTRL, VE_DEC_H265_CTRL_IRQ_MASK); @@ -803,9 +837,6 @@ static int cedrus_h265_start(struct cedrus_ctx *ctx) { struct cedrus_dev *dev = ctx->dev; - /* The buffer size is calculated at setup time. */ - ctx->codec.h265.mv_col_buf_size = 0; - /* Buffer is never accessed by CPU, so we can skip kernel mapping. */ ctx->codec.h265.neighbor_info_buf = dma_alloc_attrs(dev->dev, CEDRUS_H265_NEIGHBOR_INFO_BUF_SIZE, @@ -832,14 +863,24 @@ static int cedrus_h265_start(struct cedrus_ctx *ctx) static void cedrus_h265_stop(struct cedrus_ctx *ctx) { struct cedrus_dev *dev = ctx->dev; + struct cedrus_buffer *buf; + struct vb2_queue *vq; + unsigned int i; - if (ctx->codec.h265.mv_col_buf_size > 0) { - dma_free_attrs(dev->dev, ctx->codec.h265.mv_col_buf_size, - ctx->codec.h265.mv_col_buf, - ctx->codec.h265.mv_col_buf_addr, - DMA_ATTR_NO_KERNEL_MAPPING); + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + + for (i = 0; i < vq->num_buffers; i++) { + buf = vb2_to_cedrus_buffer(vb2_get_buffer(vq, i)); - ctx->codec.h265.mv_col_buf_size = 0; + if (buf->codec.h265.mv_col_buf_size > 0) { + dma_free_attrs(dev->dev, + buf->codec.h265.mv_col_buf_size, + buf->codec.h265.mv_col_buf, + buf->codec.h265.mv_col_buf_dma, + DMA_ATTR_NO_KERNEL_MAPPING); + + buf->codec.h265.mv_col_buf_size = 0; + } } dma_free_attrs(dev->dev, CEDRUS_H265_NEIGHBOR_INFO_BUF_SIZE, @@ -858,6 +899,15 @@ static void cedrus_h265_trigger(struct cedrus_ctx *ctx) cedrus_write(dev, VE_DEC_H265_TRIGGER, VE_DEC_H265_TRIGGER_DEC_SLICE); } +static unsigned int cedrus_h265_extra_cap_size(struct cedrus_ctx *ctx, + struct v4l2_pix_format *pix_fmt) +{ + if (ctx->bit_depth > 8) + return cedrus_h265_2bit_size(pix_fmt->width, pix_fmt->height); + + return 0; +} + struct cedrus_dec_ops cedrus_dec_ops_h265 = { .irq_clear = cedrus_h265_irq_clear, .irq_disable = cedrus_h265_irq_disable, @@ -866,4 +916,5 @@ struct cedrus_dec_ops cedrus_dec_ops_h265 = { .start = cedrus_h265_start, .stop = cedrus_h265_stop, .trigger = cedrus_h265_trigger, + .extra_cap_size = cedrus_h265_extra_cap_size, }; diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c index a6470a89851e..fa86a658fdc6 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c @@ -31,7 +31,7 @@ #include "cedrus_hw.h" #include "cedrus_regs.h" -int cedrus_engine_enable(struct cedrus_ctx *ctx, enum cedrus_codec codec) +int cedrus_engine_enable(struct cedrus_ctx *ctx) { u32 reg = 0; @@ -42,18 +42,18 @@ int cedrus_engine_enable(struct cedrus_ctx *ctx, enum cedrus_codec codec) reg |= VE_MODE_REC_WR_MODE_2MB; reg |= VE_MODE_DDR_MODE_BW_128; - switch (codec) { - case CEDRUS_CODEC_MPEG2: + switch (ctx->src_fmt.pixelformat) { + case V4L2_PIX_FMT_MPEG2_SLICE: reg |= VE_MODE_DEC_MPEG; break; /* H.264 and VP8 both use the same decoding mode bit. */ - case CEDRUS_CODEC_H264: - case CEDRUS_CODEC_VP8: + case V4L2_PIX_FMT_H264_SLICE: + case V4L2_PIX_FMT_VP8_FRAME: reg |= VE_MODE_DEC_H264; break; - case CEDRUS_CODEC_H265: + case V4L2_PIX_FMT_HEVC_SLICE: reg |= VE_MODE_DEC_H265; break; @@ -132,12 +132,12 @@ static irqreturn_t cedrus_irq(int irq, void *data) return IRQ_NONE; } - status = dev->dec_ops[ctx->current_codec]->irq_status(ctx); + status = ctx->current_codec->irq_status(ctx); if (status == CEDRUS_IRQ_NONE) return IRQ_NONE; - dev->dec_ops[ctx->current_codec]->irq_disable(ctx); - dev->dec_ops[ctx->current_codec]->irq_clear(ctx); + ctx->current_codec->irq_disable(ctx); + ctx->current_codec->irq_clear(ctx); if (status == CEDRUS_IRQ_ERROR) state = VB2_BUF_STATE_ERROR; diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.h b/drivers/staging/media/sunxi/cedrus/cedrus_hw.h index 7c92f00e36da..6f1e701b1ea8 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.h @@ -16,7 +16,7 @@ #ifndef _CEDRUS_HW_H_ #define _CEDRUS_HW_H_ -int cedrus_engine_enable(struct cedrus_ctx *ctx, enum cedrus_codec codec); +int cedrus_engine_enable(struct cedrus_ctx *ctx); void cedrus_engine_disable(struct cedrus_dev *dev); void cedrus_dst_format_set(struct cedrus_dev *dev, diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c index c1128d2cd555..10e98f08aafc 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c @@ -66,7 +66,7 @@ static int cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) quantisation = run->mpeg2.quantisation; /* Activate MPEG engine. */ - cedrus_engine_enable(ctx, CEDRUS_CODEC_MPEG2); + cedrus_engine_enable(ctx); /* Set intra quantisation matrix. */ matrix = quantisation->intra_quantiser_matrix; diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h index d81f7513ade0..05e6cbc548ab 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h @@ -498,6 +498,22 @@ #define VE_DEC_H265_LOW_ADDR (VE_ENGINE_DEC_H265 + 0x80) +#define VE_DEC_H265_OFFSET_ADDR_FIRST_OUT (VE_ENGINE_DEC_H265 + 0x84) +#define VE_DEC_H265_OFFSET_ADDR_SECOND_OUT (VE_ENGINE_DEC_H265 + 0x88) + +#define VE_DEC_H265_SECOND_OUT_FMT_8BIT_PLUS_2BIT 0 +#define VE_DEC_H265_SECOND_OUT_FMT_P010 1 +#define VE_DEC_H265_SECOND_OUT_FMT_10BIT_4x4_TILED 2 + +#define VE_DEC_H265_10BIT_CONFIGURE_SECOND_OUT_FMT(v) \ + SHIFT_AND_MASK_BITS(v, 24, 23) +#define VE_DEC_H265_10BIT_CONFIGURE_SECOND_2BIT_ENABLE BIT(22) +#define VE_DEC_H265_10BIT_CONFIGURE_SECOND_2BIT_STRIDE(v) \ + SHIFT_AND_MASK_BITS(v, 21, 11) +#define VE_DEC_H265_10BIT_CONFIGURE_FIRST_2BIT_STRIDE(v) \ + SHIFT_AND_MASK_BITS(v, 10, 0) +#define VE_DEC_H265_10BIT_CONFIGURE (VE_ENGINE_DEC_H265 + 0x8c) + #define VE_DEC_H265_LOW_ADDR_PRIMARY_CHROMA(a) \ SHIFT_AND_MASK_BITS(a, 31, 24) #define VE_DEC_H265_LOW_ADDR_SECONDARY_CHROMA(a) \ @@ -505,6 +521,8 @@ #define VE_DEC_H265_LOW_ADDR_ENTRY_POINTS_BUF(a) \ SHIFT_AND_MASK_BITS(a, 7, 0) +#define VE_DEC_H265_BITS_READ (VE_ENGINE_DEC_H265 + 0xdc) + #define VE_DEC_H265_SRAM_OFFSET (VE_ENGINE_DEC_H265 + 0xe0) #define VE_DEC_H265_SRAM_OFFSET_PRED_WEIGHT_LUMA_L0 0x00 diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c index 66714609b577..b00feaf4072c 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c @@ -56,13 +56,13 @@ static struct cedrus_format cedrus_formats[] = { .capabilities = CEDRUS_CAPABILITY_VP8_DEC, }, { - .pixelformat = V4L2_PIX_FMT_NV12_32L32, + .pixelformat = V4L2_PIX_FMT_NV12, .directions = CEDRUS_DECODE_DST, + .capabilities = CEDRUS_CAPABILITY_UNTILED, }, { - .pixelformat = V4L2_PIX_FMT_NV12, + .pixelformat = V4L2_PIX_FMT_NV12_32L32, .directions = CEDRUS_DECODE_DST, - .capabilities = CEDRUS_CAPABILITY_UNTILED, }, }; @@ -73,8 +73,8 @@ static inline struct cedrus_ctx *cedrus_file2ctx(struct file *file) return container_of(file->private_data, struct cedrus_ctx, fh); } -static struct cedrus_format *cedrus_find_format(u32 pixelformat, u32 directions, - unsigned int capabilities) +static struct cedrus_format *cedrus_find_format(struct cedrus_ctx *ctx, + u32 pixelformat, u32 directions) { struct cedrus_format *first_valid_fmt = NULL; struct cedrus_format *fmt; @@ -83,7 +83,7 @@ static struct cedrus_format *cedrus_find_format(u32 pixelformat, u32 directions, for (i = 0; i < CEDRUS_FORMATS_COUNT; i++) { fmt = &cedrus_formats[i]; - if ((fmt->capabilities & capabilities) != fmt->capabilities || + if (!cedrus_is_capable(ctx, fmt->capabilities) || !(fmt->directions & directions)) continue; @@ -177,19 +177,13 @@ static int cedrus_enum_fmt(struct file *file, struct v4l2_fmtdesc *f, u32 direction) { struct cedrus_ctx *ctx = cedrus_file2ctx(file); - struct cedrus_dev *dev = ctx->dev; - unsigned int capabilities = dev->capabilities; - struct cedrus_format *fmt; unsigned int i, index; /* Index among formats that match the requested direction. */ index = 0; for (i = 0; i < CEDRUS_FORMATS_COUNT; i++) { - fmt = &cedrus_formats[i]; - - if (fmt->capabilities && (fmt->capabilities & capabilities) != - fmt->capabilities) + if (!cedrus_is_capable(ctx, cedrus_formats[i].capabilities)) continue; if (!(cedrus_formats[i].directions & direction)) @@ -241,15 +235,12 @@ static int cedrus_g_fmt_vid_out(struct file *file, void *priv, return 0; } -static int cedrus_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) +static int cedrus_try_fmt_vid_cap_p(struct cedrus_ctx *ctx, + struct v4l2_pix_format *pix_fmt) { - struct cedrus_ctx *ctx = cedrus_file2ctx(file); - struct cedrus_dev *dev = ctx->dev; - struct v4l2_pix_format *pix_fmt = &f->fmt.pix; struct cedrus_format *fmt = - cedrus_find_format(pix_fmt->pixelformat, CEDRUS_DECODE_DST, - dev->capabilities); + cedrus_find_format(ctx, pix_fmt->pixelformat, + CEDRUS_DECODE_DST); if (!fmt) return -EINVAL; @@ -259,18 +250,25 @@ static int cedrus_try_fmt_vid_cap(struct file *file, void *priv, pix_fmt->height = ctx->src_fmt.height; cedrus_prepare_format(pix_fmt); + if (ctx->current_codec->extra_cap_size) + pix_fmt->sizeimage += + ctx->current_codec->extra_cap_size(ctx, pix_fmt); + return 0; } -static int cedrus_try_fmt_vid_out(struct file *file, void *priv, +static int cedrus_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct cedrus_ctx *ctx = cedrus_file2ctx(file); - struct cedrus_dev *dev = ctx->dev; - struct v4l2_pix_format *pix_fmt = &f->fmt.pix; + return cedrus_try_fmt_vid_cap_p(cedrus_file2ctx(file), &f->fmt.pix); +} + +static int cedrus_try_fmt_vid_out_p(struct cedrus_ctx *ctx, + struct v4l2_pix_format *pix_fmt) +{ struct cedrus_format *fmt = - cedrus_find_format(pix_fmt->pixelformat, CEDRUS_DECODE_SRC, - dev->capabilities); + cedrus_find_format(ctx, pix_fmt->pixelformat, + CEDRUS_DECODE_SRC); if (!fmt) return -EINVAL; @@ -281,6 +279,12 @@ static int cedrus_try_fmt_vid_out(struct file *file, void *priv, return 0; } +static int cedrus_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + return cedrus_try_fmt_vid_out_p(cedrus_file2ctx(file), &f->fmt.pix); +} + static int cedrus_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { @@ -301,18 +305,76 @@ static int cedrus_s_fmt_vid_cap(struct file *file, void *priv, return 0; } -static int cedrus_s_fmt_vid_out(struct file *file, void *priv, - struct v4l2_format *f) +void cedrus_reset_cap_format(struct cedrus_ctx *ctx) +{ + ctx->dst_fmt.pixelformat = 0; + cedrus_try_fmt_vid_cap_p(ctx, &ctx->dst_fmt); +} + +static int cedrus_s_fmt_vid_out_p(struct cedrus_ctx *ctx, + struct v4l2_pix_format *pix_fmt) { - struct cedrus_ctx *ctx = cedrus_file2ctx(file); struct vb2_queue *vq; - struct vb2_queue *peer_vq; int ret; - ret = cedrus_try_fmt_vid_out(file, priv, f); + ret = cedrus_try_fmt_vid_out_p(ctx, pix_fmt); if (ret) return ret; + ctx->src_fmt = *pix_fmt; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + + switch (ctx->src_fmt.pixelformat) { + case V4L2_PIX_FMT_H264_SLICE: + case V4L2_PIX_FMT_HEVC_SLICE: + vq->subsystem_flags |= + VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF; + break; + default: + vq->subsystem_flags &= + ~VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF; + break; + } + + switch (ctx->src_fmt.pixelformat) { + case V4L2_PIX_FMT_MPEG2_SLICE: + ctx->current_codec = &cedrus_dec_ops_mpeg2; + break; + case V4L2_PIX_FMT_H264_SLICE: + ctx->current_codec = &cedrus_dec_ops_h264; + break; + case V4L2_PIX_FMT_HEVC_SLICE: + ctx->current_codec = &cedrus_dec_ops_h265; + break; + case V4L2_PIX_FMT_VP8_FRAME: + ctx->current_codec = &cedrus_dec_ops_vp8; + break; + } + + /* Propagate format information to capture. */ + ctx->dst_fmt.colorspace = pix_fmt->colorspace; + ctx->dst_fmt.xfer_func = pix_fmt->xfer_func; + ctx->dst_fmt.ycbcr_enc = pix_fmt->ycbcr_enc; + ctx->dst_fmt.quantization = pix_fmt->quantization; + cedrus_reset_cap_format(ctx); + + return 0; +} + +void cedrus_reset_out_format(struct cedrus_ctx *ctx) +{ + ctx->src_fmt.pixelformat = 0; + cedrus_s_fmt_vid_out_p(ctx, &ctx->src_fmt); +} + +static int cedrus_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cedrus_ctx *ctx = cedrus_file2ctx(file); + struct vb2_queue *vq; + struct vb2_queue *peer_vq; + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); /* * In order to support dynamic resolution change, @@ -332,34 +394,7 @@ static int cedrus_s_fmt_vid_out(struct file *file, void *priv, if (vb2_is_busy(peer_vq)) return -EBUSY; - ret = cedrus_try_fmt_vid_out(file, priv, f); - if (ret) - return ret; - - ctx->src_fmt = f->fmt.pix; - - switch (ctx->src_fmt.pixelformat) { - case V4L2_PIX_FMT_H264_SLICE: - case V4L2_PIX_FMT_HEVC_SLICE: - vq->subsystem_flags |= - VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF; - break; - default: - vq->subsystem_flags &= - ~VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF; - break; - } - - /* Propagate format information to capture. */ - ctx->dst_fmt.colorspace = f->fmt.pix.colorspace; - ctx->dst_fmt.xfer_func = f->fmt.pix.xfer_func; - ctx->dst_fmt.ycbcr_enc = f->fmt.pix.ycbcr_enc; - ctx->dst_fmt.quantization = f->fmt.pix.quantization; - ctx->dst_fmt.width = ctx->src_fmt.width; - ctx->dst_fmt.height = ctx->src_fmt.height; - cedrus_prepare_format(&ctx->dst_fmt); - - return 0; + return cedrus_s_fmt_vid_out_p(cedrus_file2ctx(file), &f->fmt.pix); } const struct v4l2_ioctl_ops cedrus_ioctl_ops = { @@ -475,34 +510,13 @@ static int cedrus_start_streaming(struct vb2_queue *vq, unsigned int count) struct cedrus_dev *dev = ctx->dev; int ret = 0; - switch (ctx->src_fmt.pixelformat) { - case V4L2_PIX_FMT_MPEG2_SLICE: - ctx->current_codec = CEDRUS_CODEC_MPEG2; - break; - - case V4L2_PIX_FMT_H264_SLICE: - ctx->current_codec = CEDRUS_CODEC_H264; - break; - - case V4L2_PIX_FMT_HEVC_SLICE: - ctx->current_codec = CEDRUS_CODEC_H265; - break; - - case V4L2_PIX_FMT_VP8_FRAME: - ctx->current_codec = CEDRUS_CODEC_VP8; - break; - - default: - return -EINVAL; - } - if (V4L2_TYPE_IS_OUTPUT(vq->type)) { ret = pm_runtime_resume_and_get(dev->dev); if (ret < 0) goto err_cleanup; - if (dev->dec_ops[ctx->current_codec]->start) { - ret = dev->dec_ops[ctx->current_codec]->start(ctx); + if (ctx->current_codec->start) { + ret = ctx->current_codec->start(ctx); if (ret) goto err_pm; } @@ -524,8 +538,8 @@ static void cedrus_stop_streaming(struct vb2_queue *vq) struct cedrus_dev *dev = ctx->dev; if (V4L2_TYPE_IS_OUTPUT(vq->type)) { - if (dev->dec_ops[ctx->current_codec]->stop) - dev->dec_ops[ctx->current_codec]->stop(ctx); + if (ctx->current_codec->stop) + ctx->current_codec->stop(ctx); pm_runtime_put(dev->dev); } @@ -548,7 +562,7 @@ static void cedrus_buf_request_complete(struct vb2_buffer *vb) v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl); } -static struct vb2_ops cedrus_qops = { +static const struct vb2_ops cedrus_qops = { .queue_setup = cedrus_queue_setup, .buf_prepare = cedrus_buf_prepare, .buf_queue = cedrus_buf_queue, diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.h b/drivers/staging/media/sunxi/cedrus/cedrus_video.h index 05050c0a0921..8e1afc16a6a1 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_video.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.h @@ -27,5 +27,7 @@ extern const struct v4l2_ioctl_ops cedrus_ioctl_ops; int cedrus_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq); void cedrus_prepare_format(struct v4l2_pix_format *pix_fmt); +void cedrus_reset_cap_format(struct cedrus_ctx *ctx); +void cedrus_reset_out_format(struct cedrus_ctx *ctx); #endif diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_vp8.c b/drivers/staging/media/sunxi/cedrus/cedrus_vp8.c index f7714baae37d..969677a3bbf9 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_vp8.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_vp8.c @@ -662,7 +662,7 @@ static int cedrus_vp8_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) int header_size; u32 reg; - cedrus_engine_enable(ctx, CEDRUS_CODEC_VP8); + cedrus_engine_enable(ctx); cedrus_write(dev, VE_H264_CTRL, VE_H264_CTRL_VP8); diff --git a/drivers/staging/media/sunxi/sun6i-isp/Kconfig b/drivers/staging/media/sunxi/sun6i-isp/Kconfig new file mode 100644 index 000000000000..68dcae9cd7d7 --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_SUN6I_ISP + tristate "Allwinner A31 Image Signal Processor (ISP) Driver" + depends on V4L_PLATFORM_DRIVERS && VIDEO_DEV + depends on ARCH_SUNXI || COMPILE_TEST + depends on PM && COMMON_CLK && HAS_DMA + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_VMALLOC + select V4L2_FWNODE + select REGMAP_MMIO + help + Support for the Allwinner A31 Image Signal Processor (ISP), also + found on other platforms such as the A80, A83T or V3/V3s. diff --git a/drivers/staging/media/sunxi/sun6i-isp/Makefile b/drivers/staging/media/sunxi/sun6i-isp/Makefile new file mode 100644 index 000000000000..da1034785144 --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +sun6i-isp-y += sun6i_isp.o sun6i_isp_proc.o sun6i_isp_capture.o sun6i_isp_params.o + +obj-$(CONFIG_VIDEO_SUN6I_ISP) += sun6i-isp.o diff --git a/drivers/staging/media/sunxi/sun6i-isp/TODO.txt b/drivers/staging/media/sunxi/sun6i-isp/TODO.txt new file mode 100644 index 000000000000..1e3236edc1ab --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/TODO.txt @@ -0,0 +1,6 @@ +Unstaging requirements: +- Add uAPI support and documentation for the configuration of all the hardware + modules and description of the statistics data structures; +- Add support for statistics reporting; +- Add userspace support in libcamera which demonstrates the ability to receive + statistics and adapt hardware modules configuration accordingly; diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c new file mode 100644 index 000000000000..7b7947509b69 --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c @@ -0,0 +1,555 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> + */ + +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <media/v4l2-device.h> +#include <media/v4l2-mc.h> + +#include "sun6i_isp.h" +#include "sun6i_isp_capture.h" +#include "sun6i_isp_params.h" +#include "sun6i_isp_proc.h" +#include "sun6i_isp_reg.h" + +/* Helpers */ + +u32 sun6i_isp_load_read(struct sun6i_isp_device *isp_dev, u32 offset) +{ + u32 *data = (u32 *)(isp_dev->tables.load.data + offset); + + return *data; +} + +void sun6i_isp_load_write(struct sun6i_isp_device *isp_dev, u32 offset, + u32 value) +{ + u32 *data = (u32 *)(isp_dev->tables.load.data + offset); + + *data = value; +} + +/* State */ + +/* + * The ISP works with a load buffer, which gets copied to the actual registers + * by the hardware before processing a frame when a specific flag is set. + * This is represented by tracking the ISP state in the different parts of + * the code with explicit sync points: + * - state update: to update the load buffer for the next frame if necessary; + * - state complete: to indicate that the state update was applied. + */ + +static void sun6i_isp_state_ready(struct sun6i_isp_device *isp_dev) +{ + struct regmap *regmap = isp_dev->regmap; + u32 value; + + regmap_read(regmap, SUN6I_ISP_FE_CTRL_REG, &value); + value |= SUN6I_ISP_FE_CTRL_PARA_READY; + regmap_write(regmap, SUN6I_ISP_FE_CTRL_REG, value); +} + +static void sun6i_isp_state_complete(struct sun6i_isp_device *isp_dev) +{ + unsigned long flags; + + spin_lock_irqsave(&isp_dev->state_lock, flags); + + sun6i_isp_capture_state_complete(isp_dev); + sun6i_isp_params_state_complete(isp_dev); + + spin_unlock_irqrestore(&isp_dev->state_lock, flags); +} + +void sun6i_isp_state_update(struct sun6i_isp_device *isp_dev, bool ready_hold) +{ + bool update = false; + unsigned long flags; + + spin_lock_irqsave(&isp_dev->state_lock, flags); + + sun6i_isp_capture_state_update(isp_dev, &update); + sun6i_isp_params_state_update(isp_dev, &update); + + if (update && !ready_hold) + sun6i_isp_state_ready(isp_dev); + + spin_unlock_irqrestore(&isp_dev->state_lock, flags); +} + +/* Tables */ + +static int sun6i_isp_table_setup(struct sun6i_isp_device *isp_dev, + struct sun6i_isp_table *table) +{ + table->data = dma_alloc_coherent(isp_dev->dev, table->size, + &table->address, GFP_KERNEL); + if (!table->data) + return -ENOMEM; + + return 0; +} + +static void sun6i_isp_table_cleanup(struct sun6i_isp_device *isp_dev, + struct sun6i_isp_table *table) +{ + dma_free_coherent(isp_dev->dev, table->size, table->data, + table->address); +} + +void sun6i_isp_tables_configure(struct sun6i_isp_device *isp_dev) +{ + struct regmap *regmap = isp_dev->regmap; + + regmap_write(regmap, SUN6I_ISP_REG_LOAD_ADDR_REG, + SUN6I_ISP_ADDR_VALUE(isp_dev->tables.load.address)); + + regmap_write(regmap, SUN6I_ISP_REG_SAVE_ADDR_REG, + SUN6I_ISP_ADDR_VALUE(isp_dev->tables.save.address)); + + regmap_write(regmap, SUN6I_ISP_LUT_TABLE_ADDR_REG, + SUN6I_ISP_ADDR_VALUE(isp_dev->tables.lut.address)); + + regmap_write(regmap, SUN6I_ISP_DRC_TABLE_ADDR_REG, + SUN6I_ISP_ADDR_VALUE(isp_dev->tables.drc.address)); + + regmap_write(regmap, SUN6I_ISP_STATS_ADDR_REG, + SUN6I_ISP_ADDR_VALUE(isp_dev->tables.stats.address)); +} + +static int sun6i_isp_tables_setup(struct sun6i_isp_device *isp_dev, + const struct sun6i_isp_variant *variant) +{ + struct sun6i_isp_tables *tables = &isp_dev->tables; + int ret; + + tables->load.size = variant->table_load_save_size; + ret = sun6i_isp_table_setup(isp_dev, &tables->load); + if (ret) + return ret; + + tables->save.size = variant->table_load_save_size; + ret = sun6i_isp_table_setup(isp_dev, &tables->save); + if (ret) + return ret; + + tables->lut.size = variant->table_lut_size; + ret = sun6i_isp_table_setup(isp_dev, &tables->lut); + if (ret) + return ret; + + tables->drc.size = variant->table_drc_size; + ret = sun6i_isp_table_setup(isp_dev, &tables->drc); + if (ret) + return ret; + + tables->stats.size = variant->table_stats_size; + ret = sun6i_isp_table_setup(isp_dev, &tables->stats); + if (ret) + return ret; + + return 0; +} + +static void sun6i_isp_tables_cleanup(struct sun6i_isp_device *isp_dev) +{ + struct sun6i_isp_tables *tables = &isp_dev->tables; + + sun6i_isp_table_cleanup(isp_dev, &tables->stats); + sun6i_isp_table_cleanup(isp_dev, &tables->drc); + sun6i_isp_table_cleanup(isp_dev, &tables->lut); + sun6i_isp_table_cleanup(isp_dev, &tables->save); + sun6i_isp_table_cleanup(isp_dev, &tables->load); +} + +/* Media */ + +static const struct media_device_ops sun6i_isp_media_ops = { + .link_notify = v4l2_pipeline_link_notify, +}; + +/* V4L2 */ + +static int sun6i_isp_v4l2_setup(struct sun6i_isp_device *isp_dev) +{ + struct sun6i_isp_v4l2 *v4l2 = &isp_dev->v4l2; + struct v4l2_device *v4l2_dev = &v4l2->v4l2_dev; + struct media_device *media_dev = &v4l2->media_dev; + struct device *dev = isp_dev->dev; + int ret; + + /* Media Device */ + + strscpy(media_dev->model, SUN6I_ISP_DESCRIPTION, + sizeof(media_dev->model)); + media_dev->ops = &sun6i_isp_media_ops; + media_dev->hw_revision = 0; + media_dev->dev = dev; + + media_device_init(media_dev); + + ret = media_device_register(media_dev); + if (ret) { + dev_err(dev, "failed to register media device\n"); + return ret; + } + + /* V4L2 Device */ + + v4l2_dev->mdev = media_dev; + + ret = v4l2_device_register(dev, v4l2_dev); + if (ret) { + dev_err(dev, "failed to register v4l2 device\n"); + goto error_media; + } + + return 0; + +error_media: + media_device_unregister(media_dev); + media_device_cleanup(media_dev); + + return ret; +} + +static void sun6i_isp_v4l2_cleanup(struct sun6i_isp_device *isp_dev) +{ + struct sun6i_isp_v4l2 *v4l2 = &isp_dev->v4l2; + + media_device_unregister(&v4l2->media_dev); + v4l2_device_unregister(&v4l2->v4l2_dev); + media_device_cleanup(&v4l2->media_dev); +} + +/* Platform */ + +static irqreturn_t sun6i_isp_interrupt(int irq, void *private) +{ + struct sun6i_isp_device *isp_dev = private; + struct regmap *regmap = isp_dev->regmap; + u32 status = 0, enable = 0; + + regmap_read(regmap, SUN6I_ISP_FE_INT_STA_REG, &status); + regmap_read(regmap, SUN6I_ISP_FE_INT_EN_REG, &enable); + + if (!status) + return IRQ_NONE; + else if (!(status & enable)) + goto complete; + + /* + * The ISP working cycle starts with a params-load, which makes the + * state from the load buffer active. Then it starts processing the + * frame and gives a finish interrupt. Soon after that, the next state + * coming from the load buffer will be applied for the next frame, + * giving a params-load as well. + * + * Because both frame finish and params-load are received almost + * at the same time (one ISR call), handle them in chronology order. + */ + + if (status & SUN6I_ISP_FE_INT_STA_FINISH) + sun6i_isp_capture_finish(isp_dev); + + if (status & SUN6I_ISP_FE_INT_STA_PARA_LOAD) { + sun6i_isp_state_complete(isp_dev); + sun6i_isp_state_update(isp_dev, false); + } + +complete: + regmap_write(regmap, SUN6I_ISP_FE_INT_STA_REG, status); + + return IRQ_HANDLED; +} + +static int sun6i_isp_suspend(struct device *dev) +{ + struct sun6i_isp_device *isp_dev = dev_get_drvdata(dev); + + reset_control_assert(isp_dev->reset); + clk_disable_unprepare(isp_dev->clock_ram); + clk_disable_unprepare(isp_dev->clock_mod); + + return 0; +} + +static int sun6i_isp_resume(struct device *dev) +{ + struct sun6i_isp_device *isp_dev = dev_get_drvdata(dev); + int ret; + + ret = reset_control_deassert(isp_dev->reset); + if (ret) { + dev_err(dev, "failed to deassert reset\n"); + return ret; + } + + ret = clk_prepare_enable(isp_dev->clock_mod); + if (ret) { + dev_err(dev, "failed to enable module clock\n"); + goto error_reset; + } + + ret = clk_prepare_enable(isp_dev->clock_ram); + if (ret) { + dev_err(dev, "failed to enable ram clock\n"); + goto error_clock_mod; + } + + return 0; + +error_clock_mod: + clk_disable_unprepare(isp_dev->clock_mod); + +error_reset: + reset_control_assert(isp_dev->reset); + + return ret; +} + +static const struct dev_pm_ops sun6i_isp_pm_ops = { + .runtime_suspend = sun6i_isp_suspend, + .runtime_resume = sun6i_isp_resume, +}; + +static const struct regmap_config sun6i_isp_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x400, +}; + +static int sun6i_isp_resources_setup(struct sun6i_isp_device *isp_dev, + struct platform_device *platform_dev) +{ + struct device *dev = isp_dev->dev; + void __iomem *io_base; + int irq; + int ret; + + /* Registers */ + + io_base = devm_platform_ioremap_resource(platform_dev, 0); + if (IS_ERR(io_base)) + return PTR_ERR(io_base); + + isp_dev->regmap = devm_regmap_init_mmio_clk(dev, "bus", io_base, + &sun6i_isp_regmap_config); + if (IS_ERR(isp_dev->regmap)) { + dev_err(dev, "failed to init register map\n"); + return PTR_ERR(isp_dev->regmap); + } + + /* Clocks */ + + isp_dev->clock_mod = devm_clk_get(dev, "mod"); + if (IS_ERR(isp_dev->clock_mod)) { + dev_err(dev, "failed to acquire module clock\n"); + return PTR_ERR(isp_dev->clock_mod); + } + + isp_dev->clock_ram = devm_clk_get(dev, "ram"); + if (IS_ERR(isp_dev->clock_ram)) { + dev_err(dev, "failed to acquire ram clock\n"); + return PTR_ERR(isp_dev->clock_ram); + } + + ret = clk_set_rate_exclusive(isp_dev->clock_mod, 297000000); + if (ret) { + dev_err(dev, "failed to set mod clock rate\n"); + return ret; + } + + /* Reset */ + + isp_dev->reset = devm_reset_control_get_shared(dev, NULL); + if (IS_ERR(isp_dev->reset)) { + dev_err(dev, "failed to acquire reset\n"); + ret = PTR_ERR(isp_dev->reset); + goto error_clock_rate_exclusive; + } + + /* Interrupt */ + + irq = platform_get_irq(platform_dev, 0); + if (irq < 0) { + dev_err(dev, "failed to get interrupt\n"); + ret = -ENXIO; + goto error_clock_rate_exclusive; + } + + ret = devm_request_irq(dev, irq, sun6i_isp_interrupt, IRQF_SHARED, + SUN6I_ISP_NAME, isp_dev); + if (ret) { + dev_err(dev, "failed to request interrupt\n"); + goto error_clock_rate_exclusive; + } + + /* Runtime PM */ + + pm_runtime_enable(dev); + + return 0; + +error_clock_rate_exclusive: + clk_rate_exclusive_put(isp_dev->clock_mod); + + return ret; +} + +static void sun6i_isp_resources_cleanup(struct sun6i_isp_device *isp_dev) +{ + struct device *dev = isp_dev->dev; + + pm_runtime_disable(dev); + clk_rate_exclusive_put(isp_dev->clock_mod); +} + +static int sun6i_isp_probe(struct platform_device *platform_dev) +{ + struct sun6i_isp_device *isp_dev; + struct device *dev = &platform_dev->dev; + const struct sun6i_isp_variant *variant; + int ret; + + variant = of_device_get_match_data(dev); + if (!variant) + return -EINVAL; + + isp_dev = devm_kzalloc(dev, sizeof(*isp_dev), GFP_KERNEL); + if (!isp_dev) + return -ENOMEM; + + isp_dev->dev = dev; + platform_set_drvdata(platform_dev, isp_dev); + + spin_lock_init(&isp_dev->state_lock); + + ret = sun6i_isp_resources_setup(isp_dev, platform_dev); + if (ret) + return ret; + + ret = sun6i_isp_tables_setup(isp_dev, variant); + if (ret) { + dev_err(dev, "failed to setup tables\n"); + goto error_resources; + } + + ret = sun6i_isp_v4l2_setup(isp_dev); + if (ret) { + dev_err(dev, "failed to setup v4l2\n"); + goto error_tables; + } + + ret = sun6i_isp_proc_setup(isp_dev); + if (ret) { + dev_err(dev, "failed to setup proc\n"); + goto error_v4l2; + } + + ret = sun6i_isp_capture_setup(isp_dev); + if (ret) { + dev_err(dev, "failed to setup capture\n"); + goto error_proc; + } + + ret = sun6i_isp_params_setup(isp_dev); + if (ret) { + dev_err(dev, "failed to setup params\n"); + goto error_capture; + } + + return 0; + +error_capture: + sun6i_isp_capture_cleanup(isp_dev); + +error_proc: + sun6i_isp_proc_cleanup(isp_dev); + +error_v4l2: + sun6i_isp_v4l2_cleanup(isp_dev); + +error_tables: + sun6i_isp_tables_cleanup(isp_dev); + +error_resources: + sun6i_isp_resources_cleanup(isp_dev); + + return ret; +} + +static int sun6i_isp_remove(struct platform_device *platform_dev) +{ + struct sun6i_isp_device *isp_dev = platform_get_drvdata(platform_dev); + + sun6i_isp_params_cleanup(isp_dev); + sun6i_isp_capture_cleanup(isp_dev); + sun6i_isp_proc_cleanup(isp_dev); + sun6i_isp_v4l2_cleanup(isp_dev); + sun6i_isp_tables_cleanup(isp_dev); + sun6i_isp_resources_cleanup(isp_dev); + + return 0; +} + +/* + * History of sun6i-isp: + * - sun4i-a10-isp: initial ISP tied to the CSI0 controller, + * apparently unused in software implementations; + * - sun6i-a31-isp: separate ISP loosely based on sun4i-a10-isp, + * adding extra modules and features; + * - sun9i-a80-isp: based on sun6i-a31-isp with some register offset changes + * and new modules like saturation and cnr; + * - sun8i-a23-isp/sun8i-h3-isp: based on sun9i-a80-isp with most modules + * related to raw removed; + * - sun8i-a83t-isp: based on sun9i-a80-isp with some register offset changes + * - sun8i-v3s-isp: based on sun8i-a83t-isp with a new disc module; + */ + +static const struct sun6i_isp_variant sun8i_v3s_isp_variant = { + .table_load_save_size = 0x1000, + .table_lut_size = 0xe00, + .table_drc_size = 0x600, + .table_stats_size = 0x2100, +}; + +static const struct of_device_id sun6i_isp_of_match[] = { + { + .compatible = "allwinner,sun8i-v3s-isp", + .data = &sun8i_v3s_isp_variant, + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, sun6i_isp_of_match); + +static struct platform_driver sun6i_isp_platform_driver = { + .probe = sun6i_isp_probe, + .remove = sun6i_isp_remove, + .driver = { + .name = SUN6I_ISP_NAME, + .of_match_table = of_match_ptr(sun6i_isp_of_match), + .pm = &sun6i_isp_pm_ops, + }, +}; + +module_platform_driver(sun6i_isp_platform_driver); + +MODULE_DESCRIPTION("Allwinner A31 Image Signal Processor driver"); +MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.h b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.h new file mode 100644 index 000000000000..0e5f188319ff --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> + */ + +#ifndef _SUN6I_ISP_H_ +#define _SUN6I_ISP_H_ + +#include <media/v4l2-device.h> +#include <media/videobuf2-v4l2.h> + +#include "sun6i_isp_capture.h" +#include "sun6i_isp_params.h" +#include "sun6i_isp_proc.h" + +#define SUN6I_ISP_NAME "sun6i-isp" +#define SUN6I_ISP_DESCRIPTION "Allwinner A31 ISP Device" + +enum sun6i_isp_port { + SUN6I_ISP_PORT_CSI0 = 0, + SUN6I_ISP_PORT_CSI1 = 1, +}; + +struct sun6i_isp_buffer { + struct vb2_v4l2_buffer v4l2_buffer; + struct list_head list; +}; + +struct sun6i_isp_v4l2 { + struct v4l2_device v4l2_dev; + struct media_device media_dev; +}; + +struct sun6i_isp_table { + void *data; + dma_addr_t address; + unsigned int size; +}; + +struct sun6i_isp_tables { + struct sun6i_isp_table load; + struct sun6i_isp_table save; + + struct sun6i_isp_table lut; + struct sun6i_isp_table drc; + struct sun6i_isp_table stats; +}; + +struct sun6i_isp_device { + struct device *dev; + + struct sun6i_isp_tables tables; + + struct sun6i_isp_v4l2 v4l2; + struct sun6i_isp_proc proc; + struct sun6i_isp_capture capture; + struct sun6i_isp_params params; + + struct regmap *regmap; + struct clk *clock_mod; + struct clk *clock_ram; + struct reset_control *reset; + + spinlock_t state_lock; /* State helpers lock. */ +}; + +struct sun6i_isp_variant { + unsigned int table_load_save_size; + unsigned int table_lut_size; + unsigned int table_drc_size; + unsigned int table_stats_size; +}; + +/* Helpers */ + +u32 sun6i_isp_load_read(struct sun6i_isp_device *isp_dev, u32 offset); +void sun6i_isp_load_write(struct sun6i_isp_device *isp_dev, u32 offset, + u32 value); +u32 sun6i_isp_address_value(dma_addr_t address); + +/* State */ + +void sun6i_isp_state_update(struct sun6i_isp_device *isp_dev, bool ready_hold); + +/* Tables */ + +void sun6i_isp_tables_configure(struct sun6i_isp_device *isp_dev); + +#endif diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.c b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.c new file mode 100644 index 000000000000..4b592820845a --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.c @@ -0,0 +1,742 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> + */ + +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mc.h> +#include <media/videobuf2-dma-contig.h> +#include <media/videobuf2-v4l2.h> + +#include "sun6i_isp.h" +#include "sun6i_isp_capture.h" +#include "sun6i_isp_proc.h" +#include "sun6i_isp_reg.h" + +/* Helpers */ + +void sun6i_isp_capture_dimensions(struct sun6i_isp_device *isp_dev, + unsigned int *width, unsigned int *height) +{ + if (width) + *width = isp_dev->capture.format.fmt.pix.width; + if (height) + *height = isp_dev->capture.format.fmt.pix.height; +} + +void sun6i_isp_capture_format(struct sun6i_isp_device *isp_dev, + u32 *pixelformat) +{ + if (pixelformat) + *pixelformat = isp_dev->capture.format.fmt.pix.pixelformat; +} + +/* Format */ + +static const struct sun6i_isp_capture_format sun6i_isp_capture_formats[] = { + { + .pixelformat = V4L2_PIX_FMT_NV12, + .output_format = SUN6I_ISP_OUTPUT_FMT_YUV420SP, + }, + { + .pixelformat = V4L2_PIX_FMT_NV21, + .output_format = SUN6I_ISP_OUTPUT_FMT_YVU420SP, + }, +}; + +const struct sun6i_isp_capture_format * +sun6i_isp_capture_format_find(u32 pixelformat) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(sun6i_isp_capture_formats); i++) + if (sun6i_isp_capture_formats[i].pixelformat == pixelformat) + return &sun6i_isp_capture_formats[i]; + + return NULL; +} + +/* Capture */ + +static void +sun6i_isp_capture_buffer_configure(struct sun6i_isp_device *isp_dev, + struct sun6i_isp_buffer *isp_buffer) +{ + const struct v4l2_format_info *info; + struct vb2_buffer *vb2_buffer; + unsigned int width, height; + unsigned int width_aligned; + dma_addr_t address; + u32 pixelformat; + + vb2_buffer = &isp_buffer->v4l2_buffer.vb2_buf; + address = vb2_dma_contig_plane_dma_addr(vb2_buffer, 0); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_MCH_Y_ADDR0_REG, + SUN6I_ISP_ADDR_VALUE(address)); + + sun6i_isp_capture_dimensions(isp_dev, &width, &height); + sun6i_isp_capture_format(isp_dev, &pixelformat); + + info = v4l2_format_info(pixelformat); + if (WARN_ON(!info)) + return; + + /* Stride needs to be aligned to 4. */ + width_aligned = ALIGN(width, 2); + + if (info->comp_planes > 1) { + address += info->bpp[0] * width_aligned * height; + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_MCH_U_ADDR0_REG, + SUN6I_ISP_ADDR_VALUE(address)); + } + + if (info->comp_planes > 2) { + address += info->bpp[1] * + DIV_ROUND_UP(width_aligned, info->hdiv) * + DIV_ROUND_UP(height, info->vdiv); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_MCH_V_ADDR0_REG, + SUN6I_ISP_ADDR_VALUE(address)); + } +} + +void sun6i_isp_capture_configure(struct sun6i_isp_device *isp_dev) +{ + unsigned int width, height; + unsigned int stride_luma, stride_chroma = 0; + unsigned int stride_luma_div4, stride_chroma_div4; + const struct sun6i_isp_capture_format *format; + const struct v4l2_format_info *info; + u32 pixelformat; + + sun6i_isp_capture_dimensions(isp_dev, &width, &height); + sun6i_isp_capture_format(isp_dev, &pixelformat); + + format = sun6i_isp_capture_format_find(pixelformat); + if (WARN_ON(!format)) + return; + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_MCH_SIZE_CFG_REG, + SUN6I_ISP_MCH_SIZE_CFG_WIDTH(width) | + SUN6I_ISP_MCH_SIZE_CFG_HEIGHT(height)); + + info = v4l2_format_info(pixelformat); + if (WARN_ON(!info)) + return; + + stride_luma = width * info->bpp[0]; + stride_luma_div4 = DIV_ROUND_UP(stride_luma, 4); + + if (info->comp_planes > 1) { + stride_chroma = width * info->bpp[1] / info->hdiv; + stride_chroma_div4 = DIV_ROUND_UP(stride_chroma, 4); + } + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_MCH_CFG_REG, + SUN6I_ISP_MCH_CFG_EN | + SUN6I_ISP_MCH_CFG_OUTPUT_FMT(format->output_format) | + SUN6I_ISP_MCH_CFG_STRIDE_Y_DIV4(stride_luma_div4) | + SUN6I_ISP_MCH_CFG_STRIDE_UV_DIV4(stride_chroma_div4)); +} + +/* State */ + +static void sun6i_isp_capture_state_cleanup(struct sun6i_isp_device *isp_dev, + bool error) +{ + struct sun6i_isp_capture_state *state = &isp_dev->capture.state; + struct sun6i_isp_buffer **isp_buffer_states[] = { + &state->pending, &state->current, &state->complete, + }; + struct sun6i_isp_buffer *isp_buffer; + struct vb2_buffer *vb2_buffer; + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&state->lock, flags); + + for (i = 0; i < ARRAY_SIZE(isp_buffer_states); i++) { + isp_buffer = *isp_buffer_states[i]; + if (!isp_buffer) + continue; + + vb2_buffer = &isp_buffer->v4l2_buffer.vb2_buf; + vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR : + VB2_BUF_STATE_QUEUED); + + *isp_buffer_states[i] = NULL; + } + + list_for_each_entry(isp_buffer, &state->queue, list) { + vb2_buffer = &isp_buffer->v4l2_buffer.vb2_buf; + vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR : + VB2_BUF_STATE_QUEUED); + } + + INIT_LIST_HEAD(&state->queue); + + spin_unlock_irqrestore(&state->lock, flags); +} + +void sun6i_isp_capture_state_update(struct sun6i_isp_device *isp_dev, + bool *update) +{ + struct sun6i_isp_capture_state *state = &isp_dev->capture.state; + struct sun6i_isp_buffer *isp_buffer; + unsigned long flags; + + spin_lock_irqsave(&state->lock, flags); + + if (list_empty(&state->queue)) + goto complete; + + if (state->pending) + goto complete; + + isp_buffer = list_first_entry(&state->queue, struct sun6i_isp_buffer, + list); + + sun6i_isp_capture_buffer_configure(isp_dev, isp_buffer); + + list_del(&isp_buffer->list); + + state->pending = isp_buffer; + + if (update) + *update = true; + +complete: + spin_unlock_irqrestore(&state->lock, flags); +} + +void sun6i_isp_capture_state_complete(struct sun6i_isp_device *isp_dev) +{ + struct sun6i_isp_capture_state *state = &isp_dev->capture.state; + unsigned long flags; + + spin_lock_irqsave(&state->lock, flags); + + if (!state->pending) + goto complete; + + state->complete = state->current; + state->current = state->pending; + state->pending = NULL; + + if (state->complete) { + struct sun6i_isp_buffer *isp_buffer = state->complete; + struct vb2_buffer *vb2_buffer = + &isp_buffer->v4l2_buffer.vb2_buf; + + vb2_buffer->timestamp = ktime_get_ns(); + isp_buffer->v4l2_buffer.sequence = state->sequence; + + vb2_buffer_done(vb2_buffer, VB2_BUF_STATE_DONE); + + state->complete = NULL; + } + +complete: + spin_unlock_irqrestore(&state->lock, flags); +} + +void sun6i_isp_capture_finish(struct sun6i_isp_device *isp_dev) +{ + struct sun6i_isp_capture_state *state = &isp_dev->capture.state; + unsigned long flags; + + spin_lock_irqsave(&state->lock, flags); + state->sequence++; + spin_unlock_irqrestore(&state->lock, flags); +} + +/* Queue */ + +static int sun6i_isp_capture_queue_setup(struct vb2_queue *queue, + unsigned int *buffers_count, + unsigned int *planes_count, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct sun6i_isp_device *isp_dev = vb2_get_drv_priv(queue); + unsigned int size = isp_dev->capture.format.fmt.pix.sizeimage; + + if (*planes_count) + return sizes[0] < size ? -EINVAL : 0; + + *planes_count = 1; + sizes[0] = size; + + return 0; +} + +static int sun6i_isp_capture_buffer_prepare(struct vb2_buffer *vb2_buffer) +{ + struct sun6i_isp_device *isp_dev = + vb2_get_drv_priv(vb2_buffer->vb2_queue); + struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev; + unsigned int size = isp_dev->capture.format.fmt.pix.sizeimage; + + if (vb2_plane_size(vb2_buffer, 0) < size) { + v4l2_err(v4l2_dev, "buffer too small (%lu < %u)\n", + vb2_plane_size(vb2_buffer, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb2_buffer, 0, size); + + return 0; +} + +static void sun6i_isp_capture_buffer_queue(struct vb2_buffer *vb2_buffer) +{ + struct sun6i_isp_device *isp_dev = + vb2_get_drv_priv(vb2_buffer->vb2_queue); + struct sun6i_isp_capture_state *state = &isp_dev->capture.state; + struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(vb2_buffer); + struct sun6i_isp_buffer *isp_buffer = + container_of(v4l2_buffer, struct sun6i_isp_buffer, v4l2_buffer); + unsigned long flags; + + spin_lock_irqsave(&state->lock, flags); + list_add_tail(&isp_buffer->list, &state->queue); + spin_unlock_irqrestore(&state->lock, flags); + + /* Update the state to schedule our buffer as soon as possible. */ + if (state->streaming) + sun6i_isp_state_update(isp_dev, false); +} + +static int sun6i_isp_capture_start_streaming(struct vb2_queue *queue, + unsigned int count) +{ + struct sun6i_isp_device *isp_dev = vb2_get_drv_priv(queue); + struct sun6i_isp_capture_state *state = &isp_dev->capture.state; + struct video_device *video_dev = &isp_dev->capture.video_dev; + struct v4l2_subdev *subdev = &isp_dev->proc.subdev; + int ret; + + state->sequence = 0; + + ret = video_device_pipeline_alloc_start(video_dev); + if (ret < 0) + goto error_state; + + state->streaming = true; + + ret = v4l2_subdev_call(subdev, video, s_stream, 1); + if (ret && ret != -ENOIOCTLCMD) + goto error_streaming; + + return 0; + +error_streaming: + state->streaming = false; + + video_device_pipeline_stop(video_dev); + +error_state: + sun6i_isp_capture_state_cleanup(isp_dev, false); + + return ret; +} + +static void sun6i_isp_capture_stop_streaming(struct vb2_queue *queue) +{ + struct sun6i_isp_device *isp_dev = vb2_get_drv_priv(queue); + struct sun6i_isp_capture_state *state = &isp_dev->capture.state; + struct video_device *video_dev = &isp_dev->capture.video_dev; + struct v4l2_subdev *subdev = &isp_dev->proc.subdev; + + v4l2_subdev_call(subdev, video, s_stream, 0); + + state->streaming = false; + + video_device_pipeline_stop(video_dev); + + sun6i_isp_capture_state_cleanup(isp_dev, true); +} + +static const struct vb2_ops sun6i_isp_capture_queue_ops = { + .queue_setup = sun6i_isp_capture_queue_setup, + .buf_prepare = sun6i_isp_capture_buffer_prepare, + .buf_queue = sun6i_isp_capture_buffer_queue, + .start_streaming = sun6i_isp_capture_start_streaming, + .stop_streaming = sun6i_isp_capture_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +/* Video Device */ + +static void sun6i_isp_capture_format_prepare(struct v4l2_format *format) +{ + struct v4l2_pix_format *pix_format = &format->fmt.pix; + const struct v4l2_format_info *info; + unsigned int width, height; + unsigned int width_aligned; + unsigned int i; + + v4l_bound_align_image(&pix_format->width, SUN6I_ISP_CAPTURE_WIDTH_MIN, + SUN6I_ISP_CAPTURE_WIDTH_MAX, 1, + &pix_format->height, SUN6I_ISP_CAPTURE_HEIGHT_MIN, + SUN6I_ISP_CAPTURE_HEIGHT_MAX, 1, 0); + + if (!sun6i_isp_capture_format_find(pix_format->pixelformat)) + pix_format->pixelformat = + sun6i_isp_capture_formats[0].pixelformat; + + info = v4l2_format_info(pix_format->pixelformat); + if (WARN_ON(!info)) + return; + + width = pix_format->width; + height = pix_format->height; + + /* Stride needs to be aligned to 4. */ + width_aligned = ALIGN(width, 2); + + pix_format->bytesperline = width_aligned * info->bpp[0]; + pix_format->sizeimage = 0; + + for (i = 0; i < info->comp_planes; i++) { + unsigned int hdiv = (i == 0) ? 1 : info->hdiv; + unsigned int vdiv = (i == 0) ? 1 : info->vdiv; + + pix_format->sizeimage += info->bpp[i] * + DIV_ROUND_UP(width_aligned, hdiv) * + DIV_ROUND_UP(height, vdiv); + } + + pix_format->field = V4L2_FIELD_NONE; + + pix_format->colorspace = V4L2_COLORSPACE_RAW; + pix_format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + pix_format->quantization = V4L2_QUANTIZATION_DEFAULT; + pix_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static int sun6i_isp_capture_querycap(struct file *file, void *private, + struct v4l2_capability *capability) +{ + struct sun6i_isp_device *isp_dev = video_drvdata(file); + struct video_device *video_dev = &isp_dev->capture.video_dev; + + strscpy(capability->driver, SUN6I_ISP_NAME, sizeof(capability->driver)); + strscpy(capability->card, video_dev->name, sizeof(capability->card)); + snprintf(capability->bus_info, sizeof(capability->bus_info), + "platform:%s", dev_name(isp_dev->dev)); + + return 0; +} + +static int sun6i_isp_capture_enum_fmt(struct file *file, void *private, + struct v4l2_fmtdesc *fmtdesc) +{ + u32 index = fmtdesc->index; + + if (index >= ARRAY_SIZE(sun6i_isp_capture_formats)) + return -EINVAL; + + fmtdesc->pixelformat = sun6i_isp_capture_formats[index].pixelformat; + + return 0; +} + +static int sun6i_isp_capture_g_fmt(struct file *file, void *private, + struct v4l2_format *format) +{ + struct sun6i_isp_device *isp_dev = video_drvdata(file); + + *format = isp_dev->capture.format; + + return 0; +} + +static int sun6i_isp_capture_s_fmt(struct file *file, void *private, + struct v4l2_format *format) +{ + struct sun6i_isp_device *isp_dev = video_drvdata(file); + + if (vb2_is_busy(&isp_dev->capture.queue)) + return -EBUSY; + + sun6i_isp_capture_format_prepare(format); + + isp_dev->capture.format = *format; + + return 0; +} + +static int sun6i_isp_capture_try_fmt(struct file *file, void *private, + struct v4l2_format *format) +{ + sun6i_isp_capture_format_prepare(format); + + return 0; +} + +static int sun6i_isp_capture_enum_input(struct file *file, void *private, + struct v4l2_input *input) +{ + if (input->index != 0) + return -EINVAL; + + input->type = V4L2_INPUT_TYPE_CAMERA; + strscpy(input->name, "Camera", sizeof(input->name)); + + return 0; +} + +static int sun6i_isp_capture_g_input(struct file *file, void *private, + unsigned int *index) +{ + *index = 0; + + return 0; +} + +static int sun6i_isp_capture_s_input(struct file *file, void *private, + unsigned int index) +{ + if (index != 0) + return -EINVAL; + + return 0; +} + +static const struct v4l2_ioctl_ops sun6i_isp_capture_ioctl_ops = { + .vidioc_querycap = sun6i_isp_capture_querycap, + + .vidioc_enum_fmt_vid_cap = sun6i_isp_capture_enum_fmt, + .vidioc_g_fmt_vid_cap = sun6i_isp_capture_g_fmt, + .vidioc_s_fmt_vid_cap = sun6i_isp_capture_s_fmt, + .vidioc_try_fmt_vid_cap = sun6i_isp_capture_try_fmt, + + .vidioc_enum_input = sun6i_isp_capture_enum_input, + .vidioc_g_input = sun6i_isp_capture_g_input, + .vidioc_s_input = sun6i_isp_capture_s_input, + + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +static int sun6i_isp_capture_open(struct file *file) +{ + struct sun6i_isp_device *isp_dev = video_drvdata(file); + struct video_device *video_dev = &isp_dev->capture.video_dev; + struct mutex *lock = &isp_dev->capture.lock; + int ret; + + if (mutex_lock_interruptible(lock)) + return -ERESTARTSYS; + + ret = v4l2_pipeline_pm_get(&video_dev->entity); + if (ret) + goto error_mutex; + + ret = v4l2_fh_open(file); + if (ret) + goto error_pipeline; + + mutex_unlock(lock); + + return 0; + +error_pipeline: + v4l2_pipeline_pm_put(&video_dev->entity); + +error_mutex: + mutex_unlock(lock); + + return ret; +} + +static int sun6i_isp_capture_release(struct file *file) +{ + struct sun6i_isp_device *isp_dev = video_drvdata(file); + struct video_device *video_dev = &isp_dev->capture.video_dev; + struct mutex *lock = &isp_dev->capture.lock; + + mutex_lock(lock); + + _vb2_fop_release(file, NULL); + v4l2_pipeline_pm_put(&video_dev->entity); + + mutex_unlock(lock); + + return 0; +} + +static const struct v4l2_file_operations sun6i_isp_capture_fops = { + .owner = THIS_MODULE, + .open = sun6i_isp_capture_open, + .release = sun6i_isp_capture_release, + .unlocked_ioctl = video_ioctl2, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, +}; + +/* Media Entity */ + +static int sun6i_isp_capture_link_validate(struct media_link *link) +{ + struct video_device *video_dev = + media_entity_to_video_device(link->sink->entity); + struct sun6i_isp_device *isp_dev = video_get_drvdata(video_dev); + struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev; + unsigned int capture_width, capture_height; + unsigned int proc_width, proc_height; + + sun6i_isp_capture_dimensions(isp_dev, &capture_width, &capture_height); + sun6i_isp_proc_dimensions(isp_dev, &proc_width, &proc_height); + + /* No cropping/scaling is supported (yet). */ + if (capture_width != proc_width || capture_height != proc_height) { + v4l2_err(v4l2_dev, + "invalid input/output dimensions: %ux%u/%ux%u\n", + proc_width, proc_height, capture_width, + capture_height); + return -EINVAL; + } + + return 0; +} + +static const struct media_entity_operations sun6i_isp_capture_entity_ops = { + .link_validate = sun6i_isp_capture_link_validate, +}; + +/* Capture */ + +int sun6i_isp_capture_setup(struct sun6i_isp_device *isp_dev) +{ + struct sun6i_isp_capture *capture = &isp_dev->capture; + struct sun6i_isp_capture_state *state = &capture->state; + struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev; + struct v4l2_subdev *proc_subdev = &isp_dev->proc.subdev; + struct video_device *video_dev = &capture->video_dev; + struct vb2_queue *queue = &capture->queue; + struct media_pad *pad = &capture->pad; + struct v4l2_format *format = &capture->format; + struct v4l2_pix_format *pix_format = &format->fmt.pix; + int ret; + + /* State */ + + INIT_LIST_HEAD(&state->queue); + spin_lock_init(&state->lock); + + /* Media Entity */ + + video_dev->entity.ops = &sun6i_isp_capture_entity_ops; + + /* Media Pads */ + + pad->flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; + + ret = media_entity_pads_init(&video_dev->entity, 1, pad); + if (ret) + goto error_mutex; + + /* Queue */ + + mutex_init(&capture->lock); + + queue->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + queue->io_modes = VB2_MMAP | VB2_DMABUF; + queue->buf_struct_size = sizeof(struct sun6i_isp_buffer); + queue->ops = &sun6i_isp_capture_queue_ops; + queue->mem_ops = &vb2_dma_contig_memops; + queue->min_buffers_needed = 2; + queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + queue->lock = &capture->lock; + queue->dev = isp_dev->dev; + queue->drv_priv = isp_dev; + + ret = vb2_queue_init(queue); + if (ret) { + v4l2_err(v4l2_dev, "failed to initialize vb2 queue: %d\n", ret); + goto error_media_entity; + } + + /* V4L2 Format */ + + format->type = queue->type; + pix_format->pixelformat = sun6i_isp_capture_formats[0].pixelformat; + pix_format->width = 1280; + pix_format->height = 720; + + sun6i_isp_capture_format_prepare(format); + + /* Video Device */ + + strscpy(video_dev->name, SUN6I_ISP_CAPTURE_NAME, + sizeof(video_dev->name)); + video_dev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + video_dev->vfl_dir = VFL_DIR_RX; + video_dev->release = video_device_release_empty; + video_dev->fops = &sun6i_isp_capture_fops; + video_dev->ioctl_ops = &sun6i_isp_capture_ioctl_ops; + video_dev->v4l2_dev = v4l2_dev; + video_dev->queue = queue; + video_dev->lock = &capture->lock; + + video_set_drvdata(video_dev, isp_dev); + + ret = video_register_device(video_dev, VFL_TYPE_VIDEO, -1); + if (ret) { + v4l2_err(v4l2_dev, "failed to register video device: %d\n", + ret); + goto error_media_entity; + } + + /* Media Pad Link */ + + ret = media_create_pad_link(&proc_subdev->entity, + SUN6I_ISP_PROC_PAD_SOURCE, + &video_dev->entity, 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret < 0) { + v4l2_err(v4l2_dev, "failed to create %s:%u -> %s:%u link\n", + proc_subdev->entity.name, SUN6I_ISP_PROC_PAD_SOURCE, + video_dev->entity.name, 0); + goto error_video_device; + } + + return 0; + +error_video_device: + vb2_video_unregister_device(video_dev); + +error_media_entity: + media_entity_cleanup(&video_dev->entity); + +error_mutex: + mutex_destroy(&capture->lock); + + return ret; +} + +void sun6i_isp_capture_cleanup(struct sun6i_isp_device *isp_dev) +{ + struct sun6i_isp_capture *capture = &isp_dev->capture; + struct video_device *video_dev = &capture->video_dev; + + vb2_video_unregister_device(video_dev); + media_entity_cleanup(&video_dev->entity); + mutex_destroy(&capture->lock); +} diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.h b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.h new file mode 100644 index 000000000000..0e3e4fa7a0f4 --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> + */ + +#ifndef _SUN6I_ISP_CAPTURE_H_ +#define _SUN6I_ISP_CAPTURE_H_ + +#include <media/v4l2-device.h> + +#define SUN6I_ISP_CAPTURE_NAME "sun6i-isp-capture" + +#define SUN6I_ISP_CAPTURE_WIDTH_MIN 16 +#define SUN6I_ISP_CAPTURE_WIDTH_MAX 3264 +#define SUN6I_ISP_CAPTURE_HEIGHT_MIN 16 +#define SUN6I_ISP_CAPTURE_HEIGHT_MAX 2448 + +struct sun6i_isp_device; + +struct sun6i_isp_capture_format { + u32 pixelformat; + u8 output_format; +}; + +#undef current +struct sun6i_isp_capture_state { + struct list_head queue; + spinlock_t lock; /* Queue and buffers lock. */ + + struct sun6i_isp_buffer *pending; + struct sun6i_isp_buffer *current; + struct sun6i_isp_buffer *complete; + + unsigned int sequence; + bool streaming; +}; + +struct sun6i_isp_capture { + struct sun6i_isp_capture_state state; + + struct video_device video_dev; + struct vb2_queue queue; + struct mutex lock; /* Queue lock. */ + struct media_pad pad; + + struct v4l2_format format; +}; + +/* Helpers */ + +void sun6i_isp_capture_dimensions(struct sun6i_isp_device *isp_dev, + unsigned int *width, unsigned int *height); +void sun6i_isp_capture_format(struct sun6i_isp_device *isp_dev, + u32 *pixelformat); + +/* Format */ + +const struct sun6i_isp_capture_format * +sun6i_isp_capture_format_find(u32 pixelformat); + +/* Capture */ + +void sun6i_isp_capture_configure(struct sun6i_isp_device *isp_dev); + +/* State */ + +void sun6i_isp_capture_state_update(struct sun6i_isp_device *isp_dev, + bool *update); +void sun6i_isp_capture_state_complete(struct sun6i_isp_device *isp_dev); +void sun6i_isp_capture_finish(struct sun6i_isp_device *isp_dev); + +/* Capture */ + +int sun6i_isp_capture_setup(struct sun6i_isp_device *isp_dev); +void sun6i_isp_capture_cleanup(struct sun6i_isp_device *isp_dev); + +#endif diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.c b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.c new file mode 100644 index 000000000000..8039e311cb1c --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.c @@ -0,0 +1,566 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> + */ + +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mc.h> +#include <media/videobuf2-vmalloc.h> +#include <media/videobuf2-v4l2.h> + +#include "sun6i_isp.h" +#include "sun6i_isp_params.h" +#include "sun6i_isp_reg.h" +#include "uapi/sun6i-isp-config.h" + +/* Params */ + +static const struct sun6i_isp_params_config sun6i_isp_params_config_default = { + .modules_used = SUN6I_ISP_MODULE_BAYER, + + .bayer = { + .offset_r = 32, + .offset_gr = 32, + .offset_gb = 32, + .offset_b = 32, + + .gain_r = 256, + .gain_gr = 256, + .gain_gb = 256, + .gain_b = 256, + + }, + + .bdnf = { + .in_dis_min = 8, + .in_dis_max = 16, + + .coefficients_g = { 15, 4, 1 }, + .coefficients_rb = { 15, 4 }, + }, +}; + +static void sun6i_isp_params_configure_ob(struct sun6i_isp_device *isp_dev) +{ + unsigned int width, height; + + sun6i_isp_proc_dimensions(isp_dev, &width, &height); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_OB_SIZE_REG, + SUN6I_ISP_OB_SIZE_WIDTH(width) | + SUN6I_ISP_OB_SIZE_HEIGHT(height)); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_OB_VALID_REG, + SUN6I_ISP_OB_VALID_WIDTH(width) | + SUN6I_ISP_OB_VALID_HEIGHT(height)); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_OB_SRC0_VALID_START_REG, + SUN6I_ISP_OB_SRC0_VALID_START_HORZ(0) | + SUN6I_ISP_OB_SRC0_VALID_START_VERT(0)); +} + +static void sun6i_isp_params_configure_ae(struct sun6i_isp_device *isp_dev) +{ + /* These are default values that need to be set to get an output. */ + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_AE_CFG_REG, + SUN6I_ISP_AE_CFG_LOW_BRI_TH(0xff) | + SUN6I_ISP_AE_CFG_HORZ_NUM(8) | + SUN6I_ISP_AE_CFG_HIGH_BRI_TH(0xf00) | + SUN6I_ISP_AE_CFG_VERT_NUM(8)); +} + +static void +sun6i_isp_params_configure_bayer(struct sun6i_isp_device *isp_dev, + const struct sun6i_isp_params_config *config) +{ + const struct sun6i_isp_params_config_bayer *bayer = &config->bayer; + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_BAYER_OFFSET0_REG, + SUN6I_ISP_BAYER_OFFSET0_R(bayer->offset_r) | + SUN6I_ISP_BAYER_OFFSET0_GR(bayer->offset_gr)); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_BAYER_OFFSET1_REG, + SUN6I_ISP_BAYER_OFFSET1_GB(bayer->offset_gb) | + SUN6I_ISP_BAYER_OFFSET1_B(bayer->offset_b)); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_BAYER_GAIN0_REG, + SUN6I_ISP_BAYER_GAIN0_R(bayer->gain_r) | + SUN6I_ISP_BAYER_GAIN0_GR(bayer->gain_gr)); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_BAYER_GAIN1_REG, + SUN6I_ISP_BAYER_GAIN1_GB(bayer->gain_gb) | + SUN6I_ISP_BAYER_GAIN1_B(bayer->gain_b)); +} + +static void sun6i_isp_params_configure_wb(struct sun6i_isp_device *isp_dev) +{ + /* These are default values that need to be set to get an output. */ + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_WB_GAIN0_REG, + SUN6I_ISP_WB_GAIN0_R(256) | + SUN6I_ISP_WB_GAIN0_GR(256)); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_WB_GAIN1_REG, + SUN6I_ISP_WB_GAIN1_GB(256) | + SUN6I_ISP_WB_GAIN1_B(256)); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_WB_CFG_REG, + SUN6I_ISP_WB_CFG_CLIP(0xfff)); +} + +static void sun6i_isp_params_configure_base(struct sun6i_isp_device *isp_dev) +{ + sun6i_isp_params_configure_ae(isp_dev); + sun6i_isp_params_configure_ob(isp_dev); + sun6i_isp_params_configure_wb(isp_dev); +} + +static void +sun6i_isp_params_configure_bdnf(struct sun6i_isp_device *isp_dev, + const struct sun6i_isp_params_config *config) +{ + const struct sun6i_isp_params_config_bdnf *bdnf = &config->bdnf; + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_BDNF_CFG_REG, + SUN6I_ISP_BDNF_CFG_IN_DIS_MIN(bdnf->in_dis_min) | + SUN6I_ISP_BDNF_CFG_IN_DIS_MAX(bdnf->in_dis_max)); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_BDNF_COEF_RB_REG, + SUN6I_ISP_BDNF_COEF_RB(0, bdnf->coefficients_rb[0]) | + SUN6I_ISP_BDNF_COEF_RB(1, bdnf->coefficients_rb[1]) | + SUN6I_ISP_BDNF_COEF_RB(2, bdnf->coefficients_rb[2]) | + SUN6I_ISP_BDNF_COEF_RB(3, bdnf->coefficients_rb[3]) | + SUN6I_ISP_BDNF_COEF_RB(4, bdnf->coefficients_rb[4])); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_BDNF_COEF_G_REG, + SUN6I_ISP_BDNF_COEF_G(0, bdnf->coefficients_g[0]) | + SUN6I_ISP_BDNF_COEF_G(1, bdnf->coefficients_g[1]) | + SUN6I_ISP_BDNF_COEF_G(2, bdnf->coefficients_g[2]) | + SUN6I_ISP_BDNF_COEF_G(3, bdnf->coefficients_g[3]) | + SUN6I_ISP_BDNF_COEF_G(4, bdnf->coefficients_g[4]) | + SUN6I_ISP_BDNF_COEF_G(5, bdnf->coefficients_g[5]) | + SUN6I_ISP_BDNF_COEF_G(6, bdnf->coefficients_g[6])); +} + +static void +sun6i_isp_params_configure_modules(struct sun6i_isp_device *isp_dev, + const struct sun6i_isp_params_config *config) +{ + u32 value; + + if (config->modules_used & SUN6I_ISP_MODULE_BDNF) + sun6i_isp_params_configure_bdnf(isp_dev, config); + + if (config->modules_used & SUN6I_ISP_MODULE_BAYER) + sun6i_isp_params_configure_bayer(isp_dev, config); + + value = sun6i_isp_load_read(isp_dev, SUN6I_ISP_MODULE_EN_REG); + /* Clear all modules but keep input configuration. */ + value &= SUN6I_ISP_MODULE_EN_SRC0 | SUN6I_ISP_MODULE_EN_SRC1; + + if (config->modules_used & SUN6I_ISP_MODULE_BDNF) + value |= SUN6I_ISP_MODULE_EN_BDNF; + + /* Bayer stage is always enabled. */ + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_MODULE_EN_REG, value); +} + +void sun6i_isp_params_configure(struct sun6i_isp_device *isp_dev) +{ + struct sun6i_isp_params_state *state = &isp_dev->params.state; + unsigned long flags; + + spin_lock_irqsave(&state->lock, flags); + + sun6i_isp_params_configure_base(isp_dev); + + /* Default config is only applied at the very first stream start. */ + if (state->configured) + goto complete; + + sun6i_isp_params_configure_modules(isp_dev, + &sun6i_isp_params_config_default); + + state->configured = true; + +complete: + spin_unlock_irqrestore(&state->lock, flags); +} + +/* State */ + +static void sun6i_isp_params_state_cleanup(struct sun6i_isp_device *isp_dev, + bool error) +{ + struct sun6i_isp_params_state *state = &isp_dev->params.state; + struct sun6i_isp_buffer *isp_buffer; + struct vb2_buffer *vb2_buffer; + unsigned long flags; + + spin_lock_irqsave(&state->lock, flags); + + if (state->pending) { + vb2_buffer = &state->pending->v4l2_buffer.vb2_buf; + vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR : + VB2_BUF_STATE_QUEUED); + } + + list_for_each_entry(isp_buffer, &state->queue, list) { + vb2_buffer = &isp_buffer->v4l2_buffer.vb2_buf; + vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR : + VB2_BUF_STATE_QUEUED); + } + + INIT_LIST_HEAD(&state->queue); + + spin_unlock_irqrestore(&state->lock, flags); +} + +void sun6i_isp_params_state_update(struct sun6i_isp_device *isp_dev, + bool *update) +{ + struct sun6i_isp_params_state *state = &isp_dev->params.state; + struct sun6i_isp_buffer *isp_buffer; + struct vb2_buffer *vb2_buffer; + const struct sun6i_isp_params_config *config; + unsigned long flags; + + spin_lock_irqsave(&state->lock, flags); + + if (list_empty(&state->queue)) + goto complete; + + if (state->pending) + goto complete; + + isp_buffer = list_first_entry(&state->queue, struct sun6i_isp_buffer, + list); + + vb2_buffer = &isp_buffer->v4l2_buffer.vb2_buf; + config = vb2_plane_vaddr(vb2_buffer, 0); + + sun6i_isp_params_configure_modules(isp_dev, config); + + list_del(&isp_buffer->list); + + state->pending = isp_buffer; + + if (update) + *update = true; + +complete: + spin_unlock_irqrestore(&state->lock, flags); +} + +void sun6i_isp_params_state_complete(struct sun6i_isp_device *isp_dev) +{ + struct sun6i_isp_params_state *state = &isp_dev->params.state; + struct sun6i_isp_buffer *isp_buffer; + struct vb2_buffer *vb2_buffer; + unsigned long flags; + + spin_lock_irqsave(&state->lock, flags); + + if (!state->pending) + goto complete; + + isp_buffer = state->pending; + vb2_buffer = &isp_buffer->v4l2_buffer.vb2_buf; + + vb2_buffer->timestamp = ktime_get_ns(); + + /* Parameters will be applied starting from the next frame. */ + isp_buffer->v4l2_buffer.sequence = isp_dev->capture.state.sequence + 1; + + vb2_buffer_done(vb2_buffer, VB2_BUF_STATE_DONE); + + state->pending = NULL; + +complete: + spin_unlock_irqrestore(&state->lock, flags); +} + +/* Queue */ + +static int sun6i_isp_params_queue_setup(struct vb2_queue *queue, + unsigned int *buffers_count, + unsigned int *planes_count, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct sun6i_isp_device *isp_dev = vb2_get_drv_priv(queue); + unsigned int size = isp_dev->params.format.fmt.meta.buffersize; + + if (*planes_count) + return sizes[0] < size ? -EINVAL : 0; + + *planes_count = 1; + sizes[0] = size; + + return 0; +} + +static int sun6i_isp_params_buffer_prepare(struct vb2_buffer *vb2_buffer) +{ + struct sun6i_isp_device *isp_dev = + vb2_get_drv_priv(vb2_buffer->vb2_queue); + struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev; + unsigned int size = isp_dev->params.format.fmt.meta.buffersize; + + if (vb2_plane_size(vb2_buffer, 0) < size) { + v4l2_err(v4l2_dev, "buffer too small (%lu < %u)\n", + vb2_plane_size(vb2_buffer, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb2_buffer, 0, size); + + return 0; +} + +static void sun6i_isp_params_buffer_queue(struct vb2_buffer *vb2_buffer) +{ + struct sun6i_isp_device *isp_dev = + vb2_get_drv_priv(vb2_buffer->vb2_queue); + struct sun6i_isp_params_state *state = &isp_dev->params.state; + struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(vb2_buffer); + struct sun6i_isp_buffer *isp_buffer = + container_of(v4l2_buffer, struct sun6i_isp_buffer, v4l2_buffer); + bool capture_streaming = isp_dev->capture.state.streaming; + unsigned long flags; + + spin_lock_irqsave(&state->lock, flags); + list_add_tail(&isp_buffer->list, &state->queue); + spin_unlock_irqrestore(&state->lock, flags); + + if (state->streaming && capture_streaming) + sun6i_isp_state_update(isp_dev, false); +} + +static int sun6i_isp_params_start_streaming(struct vb2_queue *queue, + unsigned int count) +{ + struct sun6i_isp_device *isp_dev = vb2_get_drv_priv(queue); + struct sun6i_isp_params_state *state = &isp_dev->params.state; + bool capture_streaming = isp_dev->capture.state.streaming; + + state->streaming = true; + + /* + * Update the state as soon as possible if capture is streaming, + * otherwise it will be applied when capture starts streaming. + */ + + if (capture_streaming) + sun6i_isp_state_update(isp_dev, false); + + return 0; +} + +static void sun6i_isp_params_stop_streaming(struct vb2_queue *queue) +{ + struct sun6i_isp_device *isp_dev = vb2_get_drv_priv(queue); + struct sun6i_isp_params_state *state = &isp_dev->params.state; + + state->streaming = false; + sun6i_isp_params_state_cleanup(isp_dev, true); +} + +static const struct vb2_ops sun6i_isp_params_queue_ops = { + .queue_setup = sun6i_isp_params_queue_setup, + .buf_prepare = sun6i_isp_params_buffer_prepare, + .buf_queue = sun6i_isp_params_buffer_queue, + .start_streaming = sun6i_isp_params_start_streaming, + .stop_streaming = sun6i_isp_params_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +/* Video Device */ + +static int sun6i_isp_params_querycap(struct file *file, void *private, + struct v4l2_capability *capability) +{ + struct sun6i_isp_device *isp_dev = video_drvdata(file); + struct video_device *video_dev = &isp_dev->params.video_dev; + + strscpy(capability->driver, SUN6I_ISP_NAME, sizeof(capability->driver)); + strscpy(capability->card, video_dev->name, sizeof(capability->card)); + snprintf(capability->bus_info, sizeof(capability->bus_info), + "platform:%s", dev_name(isp_dev->dev)); + + return 0; +} + +static int sun6i_isp_params_enum_fmt(struct file *file, void *private, + struct v4l2_fmtdesc *fmtdesc) +{ + struct sun6i_isp_device *isp_dev = video_drvdata(file); + struct v4l2_meta_format *params_format = + &isp_dev->params.format.fmt.meta; + + if (fmtdesc->index > 0) + return -EINVAL; + + fmtdesc->pixelformat = params_format->dataformat; + + return 0; +} + +static int sun6i_isp_params_g_fmt(struct file *file, void *private, + struct v4l2_format *format) +{ + struct sun6i_isp_device *isp_dev = video_drvdata(file); + + *format = isp_dev->params.format; + + return 0; +} + +static const struct v4l2_ioctl_ops sun6i_isp_params_ioctl_ops = { + .vidioc_querycap = sun6i_isp_params_querycap, + + .vidioc_enum_fmt_meta_out = sun6i_isp_params_enum_fmt, + .vidioc_g_fmt_meta_out = sun6i_isp_params_g_fmt, + .vidioc_s_fmt_meta_out = sun6i_isp_params_g_fmt, + .vidioc_try_fmt_meta_out = sun6i_isp_params_g_fmt, + + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +static const struct v4l2_file_operations sun6i_isp_params_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, +}; + +/* Params */ + +int sun6i_isp_params_setup(struct sun6i_isp_device *isp_dev) +{ + struct sun6i_isp_params *params = &isp_dev->params; + struct sun6i_isp_params_state *state = ¶ms->state; + struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev; + struct v4l2_subdev *proc_subdev = &isp_dev->proc.subdev; + struct video_device *video_dev = ¶ms->video_dev; + struct vb2_queue *queue = &isp_dev->params.queue; + struct media_pad *pad = &isp_dev->params.pad; + struct v4l2_format *format = &isp_dev->params.format; + struct v4l2_meta_format *params_format = &format->fmt.meta; + int ret; + + /* State */ + + INIT_LIST_HEAD(&state->queue); + spin_lock_init(&state->lock); + + /* Media Pads */ + + pad->flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT; + + ret = media_entity_pads_init(&video_dev->entity, 1, pad); + if (ret) + goto error_mutex; + + /* Queue */ + + mutex_init(¶ms->lock); + + queue->type = V4L2_BUF_TYPE_META_OUTPUT; + queue->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + queue->buf_struct_size = sizeof(struct sun6i_isp_buffer); + queue->ops = &sun6i_isp_params_queue_ops; + queue->mem_ops = &vb2_vmalloc_memops; + queue->min_buffers_needed = 1; + queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + queue->lock = ¶ms->lock; + queue->dev = isp_dev->dev; + queue->drv_priv = isp_dev; + + ret = vb2_queue_init(queue); + if (ret) { + v4l2_err(v4l2_dev, "failed to initialize vb2 queue: %d\n", ret); + goto error_media_entity; + } + + /* V4L2 Format */ + + format->type = queue->type; + params_format->dataformat = V4L2_META_FMT_SUN6I_ISP_PARAMS; + params_format->buffersize = sizeof(struct sun6i_isp_params_config); + + /* Video Device */ + + strscpy(video_dev->name, SUN6I_ISP_PARAMS_NAME, + sizeof(video_dev->name)); + video_dev->device_caps = V4L2_CAP_META_OUTPUT | V4L2_CAP_STREAMING; + video_dev->vfl_dir = VFL_DIR_TX; + video_dev->release = video_device_release_empty; + video_dev->fops = &sun6i_isp_params_fops; + video_dev->ioctl_ops = &sun6i_isp_params_ioctl_ops; + video_dev->v4l2_dev = v4l2_dev; + video_dev->queue = queue; + video_dev->lock = ¶ms->lock; + + video_set_drvdata(video_dev, isp_dev); + + ret = video_register_device(video_dev, VFL_TYPE_VIDEO, -1); + if (ret) { + v4l2_err(v4l2_dev, "failed to register video device: %d\n", + ret); + goto error_media_entity; + } + + /* Media Pad Link */ + + ret = media_create_pad_link(&video_dev->entity, 0, + &proc_subdev->entity, + SUN6I_ISP_PROC_PAD_SINK_PARAMS, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret < 0) { + v4l2_err(v4l2_dev, "failed to create %s:%u -> %s:%u link\n", + video_dev->entity.name, 0, proc_subdev->entity.name, + SUN6I_ISP_PROC_PAD_SINK_PARAMS); + goto error_video_device; + } + + return 0; + +error_video_device: + vb2_video_unregister_device(video_dev); + +error_media_entity: + media_entity_cleanup(&video_dev->entity); + +error_mutex: + mutex_destroy(¶ms->lock); + + return ret; +} + +void sun6i_isp_params_cleanup(struct sun6i_isp_device *isp_dev) +{ + struct sun6i_isp_params *params = &isp_dev->params; + struct video_device *video_dev = ¶ms->video_dev; + + vb2_video_unregister_device(video_dev); + media_entity_cleanup(&video_dev->entity); + mutex_destroy(¶ms->lock); +} diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.h b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.h new file mode 100644 index 000000000000..50f10f879c42 --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> + */ + +#ifndef _SUN6I_ISP_PARAMS_H_ +#define _SUN6I_ISP_PARAMS_H_ + +#include <media/v4l2-device.h> + +#define SUN6I_ISP_PARAMS_NAME "sun6i-isp-params" + +struct sun6i_isp_device; + +struct sun6i_isp_params_state { + struct list_head queue; /* Queue and buffers lock. */ + spinlock_t lock; + + struct sun6i_isp_buffer *pending; + + bool configured; + bool streaming; +}; + +struct sun6i_isp_params { + struct sun6i_isp_params_state state; + + struct video_device video_dev; + struct vb2_queue queue; + struct mutex lock; /* Queue lock. */ + struct media_pad pad; + + struct v4l2_format format; +}; + +/* Params */ + +void sun6i_isp_params_configure(struct sun6i_isp_device *isp_dev); + +/* State */ + +void sun6i_isp_params_state_update(struct sun6i_isp_device *isp_dev, + bool *update); +void sun6i_isp_params_state_complete(struct sun6i_isp_device *isp_dev); + +/* Params */ + +int sun6i_isp_params_setup(struct sun6i_isp_device *isp_dev); +void sun6i_isp_params_cleanup(struct sun6i_isp_device *isp_dev); + +#endif diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c new file mode 100644 index 000000000000..d69d2be0add2 --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c @@ -0,0 +1,577 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> + */ + +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#include "sun6i_isp.h" +#include "sun6i_isp_capture.h" +#include "sun6i_isp_params.h" +#include "sun6i_isp_proc.h" +#include "sun6i_isp_reg.h" + +/* Helpers */ + +void sun6i_isp_proc_dimensions(struct sun6i_isp_device *isp_dev, + unsigned int *width, unsigned int *height) +{ + if (width) + *width = isp_dev->proc.mbus_format.width; + if (height) + *height = isp_dev->proc.mbus_format.height; +} + +/* Format */ + +static const struct sun6i_isp_proc_format sun6i_isp_proc_formats[] = { + { + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, + .input_format = SUN6I_ISP_INPUT_FMT_RAW_BGGR, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, + .input_format = SUN6I_ISP_INPUT_FMT_RAW_GBRG, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, + .input_format = SUN6I_ISP_INPUT_FMT_RAW_GRBG, + }, + { + .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, + .input_format = SUN6I_ISP_INPUT_FMT_RAW_RGGB, + }, + + { + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + .input_format = SUN6I_ISP_INPUT_FMT_RAW_BGGR, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, + .input_format = SUN6I_ISP_INPUT_FMT_RAW_GBRG, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, + .input_format = SUN6I_ISP_INPUT_FMT_RAW_GRBG, + }, + { + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + .input_format = SUN6I_ISP_INPUT_FMT_RAW_RGGB, + }, +}; + +const struct sun6i_isp_proc_format *sun6i_isp_proc_format_find(u32 mbus_code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(sun6i_isp_proc_formats); i++) + if (sun6i_isp_proc_formats[i].mbus_code == mbus_code) + return &sun6i_isp_proc_formats[i]; + + return NULL; +} + +/* Processor */ + +static void sun6i_isp_proc_irq_enable(struct sun6i_isp_device *isp_dev) +{ + struct regmap *regmap = isp_dev->regmap; + + regmap_write(regmap, SUN6I_ISP_FE_INT_EN_REG, + SUN6I_ISP_FE_INT_EN_FINISH | + SUN6I_ISP_FE_INT_EN_START | + SUN6I_ISP_FE_INT_EN_PARA_SAVE | + SUN6I_ISP_FE_INT_EN_PARA_LOAD | + SUN6I_ISP_FE_INT_EN_SRC0_FIFO | + SUN6I_ISP_FE_INT_EN_ROT_FINISH); +} + +static void sun6i_isp_proc_irq_disable(struct sun6i_isp_device *isp_dev) +{ + struct regmap *regmap = isp_dev->regmap; + + regmap_write(regmap, SUN6I_ISP_FE_INT_EN_REG, 0); +} + +static void sun6i_isp_proc_irq_clear(struct sun6i_isp_device *isp_dev) +{ + struct regmap *regmap = isp_dev->regmap; + + regmap_write(regmap, SUN6I_ISP_FE_INT_EN_REG, 0); + regmap_write(regmap, SUN6I_ISP_FE_INT_STA_REG, + SUN6I_ISP_FE_INT_STA_CLEAR); +} + +static void sun6i_isp_proc_enable(struct sun6i_isp_device *isp_dev, + struct sun6i_isp_proc_source *source) +{ + struct sun6i_isp_proc *proc = &isp_dev->proc; + struct regmap *regmap = isp_dev->regmap; + u8 mode; + + /* Frontend */ + + if (source == &proc->source_csi0) + mode = SUN6I_ISP_SRC_MODE_CSI(0); + else + mode = SUN6I_ISP_SRC_MODE_CSI(1); + + regmap_write(regmap, SUN6I_ISP_FE_CFG_REG, + SUN6I_ISP_FE_CFG_EN | SUN6I_ISP_FE_CFG_SRC0_MODE(mode)); + + regmap_write(regmap, SUN6I_ISP_FE_CTRL_REG, + SUN6I_ISP_FE_CTRL_VCAP_EN | SUN6I_ISP_FE_CTRL_PARA_READY); +} + +static void sun6i_isp_proc_disable(struct sun6i_isp_device *isp_dev) +{ + struct regmap *regmap = isp_dev->regmap; + + /* Frontend */ + + regmap_write(regmap, SUN6I_ISP_FE_CTRL_REG, 0); + regmap_write(regmap, SUN6I_ISP_FE_CFG_REG, 0); +} + +static void sun6i_isp_proc_configure(struct sun6i_isp_device *isp_dev) +{ + struct v4l2_mbus_framefmt *mbus_format = &isp_dev->proc.mbus_format; + const struct sun6i_isp_proc_format *format; + u32 value; + + /* Module */ + + value = sun6i_isp_load_read(isp_dev, SUN6I_ISP_MODULE_EN_REG); + value |= SUN6I_ISP_MODULE_EN_SRC0; + sun6i_isp_load_write(isp_dev, SUN6I_ISP_MODULE_EN_REG, value); + + /* Input */ + + format = sun6i_isp_proc_format_find(mbus_format->code); + if (WARN_ON(!format)) + return; + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_MODE_REG, + SUN6I_ISP_MODE_INPUT_FMT(format->input_format) | + SUN6I_ISP_MODE_INPUT_YUV_SEQ(format->input_yuv_seq) | + SUN6I_ISP_MODE_SHARP(1) | + SUN6I_ISP_MODE_HIST(2)); +} + +/* V4L2 Subdev */ + +static int sun6i_isp_proc_s_stream(struct v4l2_subdev *subdev, int on) +{ + struct sun6i_isp_device *isp_dev = v4l2_get_subdevdata(subdev); + struct sun6i_isp_proc *proc = &isp_dev->proc; + struct media_pad *local_pad = &proc->pads[SUN6I_ISP_PROC_PAD_SINK_CSI]; + struct device *dev = isp_dev->dev; + struct sun6i_isp_proc_source *source; + struct v4l2_subdev *source_subdev; + struct media_pad *remote_pad; + /* Initialize to 0 to use both in disable label (ret != 0) and off. */ + int ret = 0; + + /* Source */ + + remote_pad = media_pad_remote_pad_unique(local_pad); + if (IS_ERR(remote_pad)) { + dev_err(dev, + "zero or more than a single source connected to the bridge\n"); + return PTR_ERR(remote_pad); + } + + source_subdev = media_entity_to_v4l2_subdev(remote_pad->entity); + + if (source_subdev == proc->source_csi0.subdev) + source = &proc->source_csi0; + else + source = &proc->source_csi1; + + if (!on) { + sun6i_isp_proc_irq_disable(isp_dev); + v4l2_subdev_call(source_subdev, video, s_stream, 0); + goto disable; + } + + /* PM */ + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + /* Clear */ + + sun6i_isp_proc_irq_clear(isp_dev); + + /* Configure */ + + sun6i_isp_tables_configure(isp_dev); + sun6i_isp_params_configure(isp_dev); + sun6i_isp_proc_configure(isp_dev); + sun6i_isp_capture_configure(isp_dev); + + /* State Update */ + + sun6i_isp_state_update(isp_dev, true); + + /* Enable */ + + sun6i_isp_proc_irq_enable(isp_dev); + sun6i_isp_proc_enable(isp_dev, source); + + ret = v4l2_subdev_call(source_subdev, video, s_stream, 1); + if (ret && ret != -ENOIOCTLCMD) { + sun6i_isp_proc_irq_disable(isp_dev); + goto disable; + } + + return 0; + +disable: + sun6i_isp_proc_disable(isp_dev); + + pm_runtime_put(dev); + + return ret; +} + +static const struct v4l2_subdev_video_ops sun6i_isp_proc_video_ops = { + .s_stream = sun6i_isp_proc_s_stream, +}; + +static void +sun6i_isp_proc_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format) +{ + if (!sun6i_isp_proc_format_find(mbus_format->code)) + mbus_format->code = sun6i_isp_proc_formats[0].mbus_code; + + mbus_format->field = V4L2_FIELD_NONE; + mbus_format->colorspace = V4L2_COLORSPACE_RAW; + mbus_format->quantization = V4L2_QUANTIZATION_DEFAULT; + mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static int sun6i_isp_proc_init_cfg(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) +{ + struct sun6i_isp_device *isp_dev = v4l2_get_subdevdata(subdev); + unsigned int pad = SUN6I_ISP_PROC_PAD_SINK_CSI; + struct v4l2_mbus_framefmt *mbus_format = + v4l2_subdev_get_try_format(subdev, state, pad); + struct mutex *lock = &isp_dev->proc.lock; + + mutex_lock(lock); + + mbus_format->code = sun6i_isp_proc_formats[0].mbus_code; + mbus_format->width = 1280; + mbus_format->height = 720; + + sun6i_isp_proc_mbus_format_prepare(mbus_format); + + mutex_unlock(lock); + + return 0; +} + +static int +sun6i_isp_proc_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code_enum) +{ + if (code_enum->index >= ARRAY_SIZE(sun6i_isp_proc_formats)) + return -EINVAL; + + code_enum->code = sun6i_isp_proc_formats[code_enum->index].mbus_code; + + return 0; +} + +static int sun6i_isp_proc_get_fmt(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct sun6i_isp_device *isp_dev = v4l2_get_subdevdata(subdev); + struct v4l2_mbus_framefmt *mbus_format = &format->format; + struct mutex *lock = &isp_dev->proc.lock; + + mutex_lock(lock); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + *mbus_format = *v4l2_subdev_get_try_format(subdev, state, + format->pad); + else + *mbus_format = isp_dev->proc.mbus_format; + + mutex_unlock(lock); + + return 0; +} + +static int sun6i_isp_proc_set_fmt(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct sun6i_isp_device *isp_dev = v4l2_get_subdevdata(subdev); + struct v4l2_mbus_framefmt *mbus_format = &format->format; + struct mutex *lock = &isp_dev->proc.lock; + + mutex_lock(lock); + + sun6i_isp_proc_mbus_format_prepare(mbus_format); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + *v4l2_subdev_get_try_format(subdev, state, format->pad) = + *mbus_format; + else + isp_dev->proc.mbus_format = *mbus_format; + + mutex_unlock(lock); + + return 0; +} + +static const struct v4l2_subdev_pad_ops sun6i_isp_proc_pad_ops = { + .init_cfg = sun6i_isp_proc_init_cfg, + .enum_mbus_code = sun6i_isp_proc_enum_mbus_code, + .get_fmt = sun6i_isp_proc_get_fmt, + .set_fmt = sun6i_isp_proc_set_fmt, +}; + +const struct v4l2_subdev_ops sun6i_isp_proc_subdev_ops = { + .video = &sun6i_isp_proc_video_ops, + .pad = &sun6i_isp_proc_pad_ops, +}; + +/* Media Entity */ + +static const struct media_entity_operations sun6i_isp_proc_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* V4L2 Async */ + +static int sun6i_isp_proc_link(struct sun6i_isp_device *isp_dev, + int sink_pad_index, + struct v4l2_subdev *remote_subdev, bool enabled) +{ + struct device *dev = isp_dev->dev; + struct v4l2_subdev *subdev = &isp_dev->proc.subdev; + struct media_entity *sink_entity = &subdev->entity; + struct media_entity *source_entity = &remote_subdev->entity; + int source_pad_index; + int ret; + + /* Get the first remote source pad. */ + ret = media_entity_get_fwnode_pad(source_entity, remote_subdev->fwnode, + MEDIA_PAD_FL_SOURCE); + if (ret < 0) { + dev_err(dev, "missing source pad in external entity %s\n", + source_entity->name); + return -EINVAL; + } + + source_pad_index = ret; + + dev_dbg(dev, "creating %s:%u -> %s:%u link\n", source_entity->name, + source_pad_index, sink_entity->name, sink_pad_index); + + ret = media_create_pad_link(source_entity, source_pad_index, + sink_entity, sink_pad_index, + enabled ? MEDIA_LNK_FL_ENABLED : 0); + if (ret < 0) { + dev_err(dev, "failed to create %s:%u -> %s:%u link\n", + source_entity->name, source_pad_index, + sink_entity->name, sink_pad_index); + return ret; + } + + return 0; +} + +static int sun6i_isp_proc_notifier_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *remote_subdev, + struct v4l2_async_subdev *async_subdev) +{ + struct sun6i_isp_device *isp_dev = + container_of(notifier, struct sun6i_isp_device, proc.notifier); + struct sun6i_isp_proc_async_subdev *proc_async_subdev = + container_of(async_subdev, struct sun6i_isp_proc_async_subdev, + async_subdev); + struct sun6i_isp_proc *proc = &isp_dev->proc; + struct sun6i_isp_proc_source *source = proc_async_subdev->source; + bool enabled; + + switch (source->endpoint.base.port) { + case SUN6I_ISP_PORT_CSI0: + source = &proc->source_csi0; + enabled = true; + break; + case SUN6I_ISP_PORT_CSI1: + source = &proc->source_csi1; + enabled = !proc->source_csi0.expected; + break; + default: + break; + } + + source->subdev = remote_subdev; + + return sun6i_isp_proc_link(isp_dev, SUN6I_ISP_PROC_PAD_SINK_CSI, + remote_subdev, enabled); +} + +static int +sun6i_isp_proc_notifier_complete(struct v4l2_async_notifier *notifier) +{ + struct sun6i_isp_device *isp_dev = + container_of(notifier, struct sun6i_isp_device, proc.notifier); + struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev; + int ret; + + ret = v4l2_device_register_subdev_nodes(v4l2_dev); + if (ret) + return ret; + + return 0; +} + +static const struct v4l2_async_notifier_operations +sun6i_isp_proc_notifier_ops = { + .bound = sun6i_isp_proc_notifier_bound, + .complete = sun6i_isp_proc_notifier_complete, +}; + +/* Processor */ + +static int sun6i_isp_proc_source_setup(struct sun6i_isp_device *isp_dev, + struct sun6i_isp_proc_source *source, + u32 port) +{ + struct device *dev = isp_dev->dev; + struct v4l2_async_notifier *notifier = &isp_dev->proc.notifier; + struct v4l2_fwnode_endpoint *endpoint = &source->endpoint; + struct sun6i_isp_proc_async_subdev *proc_async_subdev; + struct fwnode_handle *handle = NULL; + int ret; + + handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), port, 0, 0); + if (!handle) + return -ENODEV; + + ret = v4l2_fwnode_endpoint_parse(handle, endpoint); + if (ret) + goto complete; + + proc_async_subdev = + v4l2_async_nf_add_fwnode_remote(notifier, handle, + struct + sun6i_isp_proc_async_subdev); + if (IS_ERR(proc_async_subdev)) { + ret = PTR_ERR(proc_async_subdev); + goto complete; + } + + proc_async_subdev->source = source; + + source->expected = true; + +complete: + fwnode_handle_put(handle); + + return ret; +} + +int sun6i_isp_proc_setup(struct sun6i_isp_device *isp_dev) +{ + struct device *dev = isp_dev->dev; + struct sun6i_isp_proc *proc = &isp_dev->proc; + struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev; + struct v4l2_async_notifier *notifier = &proc->notifier; + struct v4l2_subdev *subdev = &proc->subdev; + struct media_pad *pads = proc->pads; + int ret; + + mutex_init(&proc->lock); + + /* V4L2 Subdev */ + + v4l2_subdev_init(subdev, &sun6i_isp_proc_subdev_ops); + strscpy(subdev->name, SUN6I_ISP_PROC_NAME, sizeof(subdev->name)); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + subdev->owner = THIS_MODULE; + subdev->dev = dev; + + v4l2_set_subdevdata(subdev, isp_dev); + + /* Media Entity */ + + subdev->entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP; + subdev->entity.ops = &sun6i_isp_proc_entity_ops; + + /* Media Pads */ + + pads[SUN6I_ISP_PROC_PAD_SINK_CSI].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_MUST_CONNECT; + pads[SUN6I_ISP_PROC_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_MUST_CONNECT; + pads[SUN6I_ISP_PROC_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&subdev->entity, SUN6I_ISP_PROC_PAD_COUNT, + pads); + if (ret) + return ret; + + /* V4L2 Subdev */ + + ret = v4l2_device_register_subdev(v4l2_dev, subdev); + if (ret < 0) { + v4l2_err(v4l2_dev, "failed to register v4l2 subdev: %d\n", ret); + goto error_media_entity; + } + + /* V4L2 Async */ + + v4l2_async_nf_init(notifier); + notifier->ops = &sun6i_isp_proc_notifier_ops; + + sun6i_isp_proc_source_setup(isp_dev, &proc->source_csi0, + SUN6I_ISP_PORT_CSI0); + sun6i_isp_proc_source_setup(isp_dev, &proc->source_csi1, + SUN6I_ISP_PORT_CSI1); + + ret = v4l2_async_nf_register(v4l2_dev, notifier); + if (ret) { + v4l2_err(v4l2_dev, + "failed to register v4l2 async notifier: %d\n", ret); + goto error_v4l2_async_notifier; + } + + return 0; + +error_v4l2_async_notifier: + v4l2_async_nf_cleanup(notifier); + + v4l2_device_unregister_subdev(subdev); + +error_media_entity: + media_entity_cleanup(&subdev->entity); + + return ret; +} + +void sun6i_isp_proc_cleanup(struct sun6i_isp_device *isp_dev) +{ + struct v4l2_async_notifier *notifier = &isp_dev->proc.notifier; + struct v4l2_subdev *subdev = &isp_dev->proc.subdev; + + v4l2_async_nf_unregister(notifier); + v4l2_async_nf_cleanup(notifier); + + v4l2_device_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); +} diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.h b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.h new file mode 100644 index 000000000000..c5c274e21ad5 --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> + */ + +#ifndef _SUN6I_ISP_PROC_H_ +#define _SUN6I_ISP_PROC_H_ + +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#define SUN6I_ISP_PROC_NAME "sun6i-isp-proc" + +enum sun6i_isp_proc_pad { + SUN6I_ISP_PROC_PAD_SINK_CSI = 0, + SUN6I_ISP_PROC_PAD_SINK_PARAMS = 1, + SUN6I_ISP_PROC_PAD_SOURCE = 2, + SUN6I_ISP_PROC_PAD_COUNT = 3, +}; + +struct sun6i_isp_device; + +struct sun6i_isp_proc_format { + u32 mbus_code; + u8 input_format; + u8 input_yuv_seq; +}; + +struct sun6i_isp_proc_source { + struct v4l2_subdev *subdev; + struct v4l2_fwnode_endpoint endpoint; + bool expected; +}; + +struct sun6i_isp_proc_async_subdev { + struct v4l2_async_subdev async_subdev; + struct sun6i_isp_proc_source *source; +}; + +struct sun6i_isp_proc { + struct v4l2_subdev subdev; + struct media_pad pads[3]; + struct v4l2_async_notifier notifier; + struct v4l2_mbus_framefmt mbus_format; + struct mutex lock; /* Mbus format lock. */ + + struct sun6i_isp_proc_source source_csi0; + struct sun6i_isp_proc_source source_csi1; +}; + +/* Helpers */ + +void sun6i_isp_proc_dimensions(struct sun6i_isp_device *isp_dev, + unsigned int *width, unsigned int *height); + +/* Format */ + +const struct sun6i_isp_proc_format *sun6i_isp_proc_format_find(u32 mbus_code); + +/* Proc */ + +int sun6i_isp_proc_setup(struct sun6i_isp_device *isp_dev); +void sun6i_isp_proc_cleanup(struct sun6i_isp_device *isp_dev); + +#endif diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_reg.h b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_reg.h new file mode 100644 index 000000000000..83b9cdab2134 --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_reg.h @@ -0,0 +1,275 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> + */ + +#ifndef _SUN6I_ISP_REG_H_ +#define _SUN6I_ISP_REG_H_ + +#include <linux/kernel.h> + +#define SUN6I_ISP_ADDR_VALUE(a) ((a) >> 2) + +/* Frontend */ + +#define SUN6I_ISP_SRC_MODE_DRAM 0 +#define SUN6I_ISP_SRC_MODE_CSI(n) (1 + (n)) + +#define SUN6I_ISP_FE_CFG_REG 0x0 +#define SUN6I_ISP_FE_CFG_EN BIT(0) +#define SUN6I_ISP_FE_CFG_SRC0_MODE(v) (((v) << 8) & GENMASK(9, 8)) +#define SUN6I_ISP_FE_CFG_SRC1_MODE(v) (((v) << 16) & GENMASK(17, 16)) + +#define SUN6I_ISP_FE_CTRL_REG 0x4 +#define SUN6I_ISP_FE_CTRL_SCAP_EN BIT(0) +#define SUN6I_ISP_FE_CTRL_VCAP_EN BIT(1) +#define SUN6I_ISP_FE_CTRL_PARA_READY BIT(2) +#define SUN6I_ISP_FE_CTRL_LUT_UPDATE BIT(3) +#define SUN6I_ISP_FE_CTRL_LENS_UPDATE BIT(4) +#define SUN6I_ISP_FE_CTRL_GAMMA_UPDATE BIT(5) +#define SUN6I_ISP_FE_CTRL_DRC_UPDATE BIT(6) +#define SUN6I_ISP_FE_CTRL_DISC_UPDATE BIT(7) +#define SUN6I_ISP_FE_CTRL_OUTPUT_SPEED_CTRL(v) (((v) << 16) & GENMASK(17, 16)) +#define SUN6I_ISP_FE_CTRL_VCAP_READ_START BIT(31) + +#define SUN6I_ISP_FE_INT_EN_REG 0x8 +#define SUN6I_ISP_FE_INT_EN_FINISH BIT(0) +#define SUN6I_ISP_FE_INT_EN_START BIT(1) +#define SUN6I_ISP_FE_INT_EN_PARA_SAVE BIT(2) +#define SUN6I_ISP_FE_INT_EN_PARA_LOAD BIT(3) +#define SUN6I_ISP_FE_INT_EN_SRC0_FIFO BIT(4) +#define SUN6I_ISP_FE_INT_EN_SRC1_FIFO BIT(5) +#define SUN6I_ISP_FE_INT_EN_ROT_FINISH BIT(6) +#define SUN6I_ISP_FE_INT_EN_LINE_NUM_START BIT(7) + +#define SUN6I_ISP_FE_INT_STA_REG 0xc +#define SUN6I_ISP_FE_INT_STA_CLEAR 0xff +#define SUN6I_ISP_FE_INT_STA_FINISH BIT(0) +#define SUN6I_ISP_FE_INT_STA_START BIT(1) +#define SUN6I_ISP_FE_INT_STA_PARA_SAVE BIT(2) +#define SUN6I_ISP_FE_INT_STA_PARA_LOAD BIT(3) +#define SUN6I_ISP_FE_INT_STA_SRC0_FIFO BIT(4) +#define SUN6I_ISP_FE_INT_STA_SRC1_FIFO BIT(5) +#define SUN6I_ISP_FE_INT_STA_ROT_FINISH BIT(6) +#define SUN6I_ISP_FE_INT_STA_LINE_NUM_START BIT(7) + +/* Only since sun9i-a80-isp. */ +#define SUN6I_ISP_FE_INT_LINE_NUM_REG 0x18 +#define SUN6I_ISP_FE_ROT_OF_CFG_REG 0x1c + +/* Buffers/tables */ + +#define SUN6I_ISP_REG_LOAD_ADDR_REG 0x20 +#define SUN6I_ISP_REG_SAVE_ADDR_REG 0x24 + +#define SUN6I_ISP_LUT_TABLE_ADDR_REG 0x28 +#define SUN6I_ISP_DRC_TABLE_ADDR_REG 0x2c +#define SUN6I_ISP_STATS_ADDR_REG 0x30 + +/* SRAM */ + +#define SUN6I_ISP_SRAM_RW_OFFSET_REG 0x38 +#define SUN6I_ISP_SRAM_RW_DATA_REG 0x3c + +/* Global */ + +#define SUN6I_ISP_MODULE_EN_REG 0x40 +#define SUN6I_ISP_MODULE_EN_AE BIT(0) +#define SUN6I_ISP_MODULE_EN_OBC BIT(1) +#define SUN6I_ISP_MODULE_EN_DPC_LUT BIT(2) +#define SUN6I_ISP_MODULE_EN_DPC_OTF BIT(3) +#define SUN6I_ISP_MODULE_EN_BDNF BIT(4) +#define SUN6I_ISP_MODULE_EN_AWB BIT(6) +#define SUN6I_ISP_MODULE_EN_WB BIT(7) +#define SUN6I_ISP_MODULE_EN_LSC BIT(8) +#define SUN6I_ISP_MODULE_EN_BGC BIT(9) +#define SUN6I_ISP_MODULE_EN_SAP BIT(10) +#define SUN6I_ISP_MODULE_EN_AF BIT(11) +#define SUN6I_ISP_MODULE_EN_RGB2RGB BIT(12) +#define SUN6I_ISP_MODULE_EN_RGB_DRC BIT(13) +#define SUN6I_ISP_MODULE_EN_TDNF BIT(15) +#define SUN6I_ISP_MODULE_EN_AFS BIT(16) +#define SUN6I_ISP_MODULE_EN_HIST BIT(17) +#define SUN6I_ISP_MODULE_EN_YUV_GAIN_OFFSET BIT(18) +#define SUN6I_ISP_MODULE_EN_YUV_DRC BIT(19) +#define SUN6I_ISP_MODULE_EN_TG BIT(20) +#define SUN6I_ISP_MODULE_EN_ROT BIT(21) +#define SUN6I_ISP_MODULE_EN_CONTRAST BIT(22) +#define SUN6I_ISP_MODULE_EN_SATU BIT(24) +#define SUN6I_ISP_MODULE_EN_SRC1 BIT(30) +#define SUN6I_ISP_MODULE_EN_SRC0 BIT(31) + +#define SUN6I_ISP_MODE_REG 0x44 +#define SUN6I_ISP_MODE_INPUT_FMT(v) ((v) & GENMASK(2, 0)) +#define SUN6I_ISP_MODE_INPUT_YUV_SEQ(v) (((v) << 3) & GENMASK(4, 3)) +#define SUN6I_ISP_MODE_OTF_DPC(v) (((v) << 16) & BIT(16)) +#define SUN6I_ISP_MODE_SHARP(v) (((v) << 17) & BIT(17)) +#define SUN6I_ISP_MODE_HIST(v) (((v) << 20) & GENMASK(21, 20)) + +#define SUN6I_ISP_INPUT_FMT_YUV420 0 +#define SUN6I_ISP_INPUT_FMT_YUV422 1 +#define SUN6I_ISP_INPUT_FMT_RAW_BGGR 4 +#define SUN6I_ISP_INPUT_FMT_RAW_RGGB 5 +#define SUN6I_ISP_INPUT_FMT_RAW_GBRG 6 +#define SUN6I_ISP_INPUT_FMT_RAW_GRBG 7 + +#define SUN6I_ISP_INPUT_YUV_SEQ_YUYV 0 +#define SUN6I_ISP_INPUT_YUV_SEQ_YVYU 1 +#define SUN6I_ISP_INPUT_YUV_SEQ_UYVY 2 +#define SUN6I_ISP_INPUT_YUV_SEQ_VYUY 3 + +#define SUN6I_ISP_IN_CFG_REG 0x48 +#define SUN6I_ISP_IN_CFG_STRIDE_DIV16(v) ((v) & GENMASK(10, 0)) + +#define SUN6I_ISP_IN_LUMA_RGB_ADDR0_REG 0x4c +#define SUN6I_ISP_IN_CHROMA_ADDR0_REG 0x50 +#define SUN6I_ISP_IN_LUMA_RGB_ADDR1_REG 0x54 +#define SUN6I_ISP_IN_CHROMA_ADDR1_REG 0x58 + +/* AE */ + +#define SUN6I_ISP_AE_CFG_REG 0x60 +#define SUN6I_ISP_AE_CFG_LOW_BRI_TH(v) ((v) & GENMASK(11, 0)) +#define SUN6I_ISP_AE_CFG_HORZ_NUM(v) (((v) << 12) & GENMASK(15, 12)) +#define SUN6I_ISP_AE_CFG_HIGH_BRI_TH(v) (((v) << 16) & GENMASK(27, 16)) +#define SUN6I_ISP_AE_CFG_VERT_NUM(v) (((v) << 28) & GENMASK(31, 28)) + +#define SUN6I_ISP_AE_SIZE_REG 0x64 +#define SUN6I_ISP_AE_SIZE_WIDTH(v) ((v) & GENMASK(10, 0)) +#define SUN6I_ISP_AE_SIZE_HEIGHT(v) (((v) << 16) & GENMASK(26, 16)) + +#define SUN6I_ISP_AE_POS_REG 0x68 +#define SUN6I_ISP_AE_POS_HORZ_START(v) ((v) & GENMASK(10, 0)) +#define SUN6I_ISP_AE_POS_VERT_START(v) (((v) << 16) & GENMASK(26, 16)) + +/* OB */ + +#define SUN6I_ISP_OB_SIZE_REG 0x78 +#define SUN6I_ISP_OB_SIZE_WIDTH(v) ((v) & GENMASK(13, 0)) +#define SUN6I_ISP_OB_SIZE_HEIGHT(v) (((v) << 16) & GENMASK(29, 16)) + +#define SUN6I_ISP_OB_VALID_REG 0x7c +#define SUN6I_ISP_OB_VALID_WIDTH(v) ((v) & GENMASK(12, 0)) +#define SUN6I_ISP_OB_VALID_HEIGHT(v) (((v) << 16) & GENMASK(28, 16)) + +#define SUN6I_ISP_OB_SRC0_VALID_START_REG 0x80 +#define SUN6I_ISP_OB_SRC0_VALID_START_HORZ(v) ((v) & GENMASK(11, 0)) +#define SUN6I_ISP_OB_SRC0_VALID_START_VERT(v) (((v) << 16) & GENMASK(27, 16)) + +#define SUN6I_ISP_OB_SRC1_VALID_START_REG 0x84 +#define SUN6I_ISP_OB_SRC1_VALID_START_HORZ(v) ((v) & GENMASK(11, 0)) +#define SUN6I_ISP_OB_SRC1_VALID_START_VERT(v) (((v) << 16) & GENMASK(27, 16)) + +#define SUN6I_ISP_OB_SPRITE_REG 0x88 +#define SUN6I_ISP_OB_SPRITE_WIDTH(v) ((v) & GENMASK(12, 0)) +#define SUN6I_ISP_OB_SPRITE_HEIGHT(v) (((v) << 16) & GENMASK(28, 16)) + +#define SUN6I_ISP_OB_SPRITE_START_REG 0x8c +#define SUN6I_ISP_OB_SPRITE_START_HORZ(v) ((v) & GENMASK(11, 0)) +#define SUN6I_ISP_OB_SPRITE_START_VERT(v) (((v) << 16) & GENMASK(27, 16)) + +#define SUN6I_ISP_OB_CFG_REG 0x90 +#define SUN6I_ISP_OB_HORZ_POS_REG 0x94 +#define SUN6I_ISP_OB_VERT_PARA_REG 0x98 +#define SUN6I_ISP_OB_OFFSET_FIXED_REG 0x9c + +/* BDNF */ + +#define SUN6I_ISP_BDNF_CFG_REG 0xcc +#define SUN6I_ISP_BDNF_CFG_IN_DIS_MIN(v) ((v) & GENMASK(7, 0)) +#define SUN6I_ISP_BDNF_CFG_IN_DIS_MAX(v) (((v) << 16) & GENMASK(23, 16)) + +#define SUN6I_ISP_BDNF_COEF_RB_REG 0xd0 +#define SUN6I_ISP_BDNF_COEF_RB(i, v) (((v) << (4 * (i))) & \ + GENMASK(4 * (i) + 3, 4 * (i))) + +#define SUN6I_ISP_BDNF_COEF_G_REG 0xd4 +#define SUN6I_ISP_BDNF_COEF_G(i, v) (((v) << (4 * (i))) & \ + GENMASK(4 * (i) + 3, 4 * (i))) + +/* Bayer */ + +#define SUN6I_ISP_BAYER_OFFSET0_REG 0xe0 +#define SUN6I_ISP_BAYER_OFFSET0_R(v) ((v) & GENMASK(12, 0)) +#define SUN6I_ISP_BAYER_OFFSET0_GR(v) (((v) << 16) & GENMASK(28, 16)) + +#define SUN6I_ISP_BAYER_OFFSET1_REG 0xe4 +#define SUN6I_ISP_BAYER_OFFSET1_GB(v) ((v) & GENMASK(12, 0)) +#define SUN6I_ISP_BAYER_OFFSET1_B(v) (((v) << 16) & GENMASK(28, 16)) + +#define SUN6I_ISP_BAYER_GAIN0_REG 0xe8 +#define SUN6I_ISP_BAYER_GAIN0_R(v) ((v) & GENMASK(11, 0)) +#define SUN6I_ISP_BAYER_GAIN0_GR(v) (((v) << 16) & GENMASK(27, 16)) + +#define SUN6I_ISP_BAYER_GAIN1_REG 0xec +#define SUN6I_ISP_BAYER_GAIN1_GB(v) ((v) & GENMASK(11, 0)) +#define SUN6I_ISP_BAYER_GAIN1_B(v) (((v) << 16) & GENMASK(27, 16)) + +/* WB */ + +#define SUN6I_ISP_WB_GAIN0_REG 0x140 +#define SUN6I_ISP_WB_GAIN0_R(v) ((v) & GENMASK(11, 0)) +#define SUN6I_ISP_WB_GAIN0_GR(v) (((v) << 16) & GENMASK(27, 16)) + +#define SUN6I_ISP_WB_GAIN1_REG 0x144 +#define SUN6I_ISP_WB_GAIN1_GB(v) ((v) & GENMASK(11, 0)) +#define SUN6I_ISP_WB_GAIN1_B(v) (((v) << 16) & GENMASK(27, 16)) + +#define SUN6I_ISP_WB_CFG_REG 0x148 +#define SUN6I_ISP_WB_CFG_CLIP(v) ((v) & GENMASK(11, 0)) + +/* Global */ + +#define SUN6I_ISP_MCH_SIZE_CFG_REG 0x1e0 +#define SUN6I_ISP_MCH_SIZE_CFG_WIDTH(v) ((v) & GENMASK(12, 0)) +#define SUN6I_ISP_MCH_SIZE_CFG_HEIGHT(v) (((v) << 16) & GENMASK(28, 16)) + +#define SUN6I_ISP_MCH_SCALE_CFG_REG 0x1e4 +#define SUN6I_ISP_MCH_SCALE_CFG_X_RATIO(v) ((v) & GENMASK(11, 0)) +#define SUN6I_ISP_MCH_SCALE_CFG_Y_RATIO(v) (((v) << 16) & GENMASK(27, 16)) +#define SUN6I_ISP_MCH_SCALE_CFG_WEIGHT_SHIFT(v) (((v) << 28) & GENMASK(31, 28)) + +#define SUN6I_ISP_SCH_SIZE_CFG_REG 0x1e8 +#define SUN6I_ISP_SCH_SIZE_CFG_WIDTH(v) ((v) & GENMASK(12, 0)) +#define SUN6I_ISP_SCH_SIZE_CFG_HEIGHT(v) (((v) << 16) & GENMASK(28, 16)) + +#define SUN6I_ISP_SCH_SCALE_CFG_REG 0x1ec +#define SUN6I_ISP_SCH_SCALE_CFG_X_RATIO(v) ((v) & GENMASK(11, 0)) +#define SUN6I_ISP_SCH_SCALE_CFG_Y_RATIO(v) (((v) << 16) & GENMASK(27, 16)) +#define SUN6I_ISP_SCH_SCALE_CFG_WEIGHT_SHIFT(v) (((v) << 28) & GENMASK(31, 28)) + +#define SUN6I_ISP_MCH_CFG_REG 0x1f0 +#define SUN6I_ISP_MCH_CFG_EN BIT(0) +#define SUN6I_ISP_MCH_CFG_SCALE_EN BIT(1) +#define SUN6I_ISP_MCH_CFG_OUTPUT_FMT(v) (((v) << 2) & GENMASK(4, 2)) +#define SUN6I_ISP_MCH_CFG_MIRROR_EN BIT(5) +#define SUN6I_ISP_MCH_CFG_FLIP_EN BIT(6) +#define SUN6I_ISP_MCH_CFG_STRIDE_Y_DIV4(v) (((v) << 8) & GENMASK(18, 8)) +#define SUN6I_ISP_MCH_CFG_STRIDE_UV_DIV4(v) (((v) << 20) & GENMASK(30, 20)) + +#define SUN6I_ISP_OUTPUT_FMT_YUV420SP 0 +#define SUN6I_ISP_OUTPUT_FMT_YUV422SP 1 +#define SUN6I_ISP_OUTPUT_FMT_YVU420SP 2 +#define SUN6I_ISP_OUTPUT_FMT_YVU422SP 3 +#define SUN6I_ISP_OUTPUT_FMT_YUV420P 4 +#define SUN6I_ISP_OUTPUT_FMT_YUV422P 5 +#define SUN6I_ISP_OUTPUT_FMT_YVU420P 6 +#define SUN6I_ISP_OUTPUT_FMT_YVU422P 7 + +#define SUN6I_ISP_SCH_CFG_REG 0x1f4 + +#define SUN6I_ISP_MCH_Y_ADDR0_REG 0x1f8 +#define SUN6I_ISP_MCH_U_ADDR0_REG 0x1fc +#define SUN6I_ISP_MCH_V_ADDR0_REG 0x200 +#define SUN6I_ISP_MCH_Y_ADDR1_REG 0x204 +#define SUN6I_ISP_MCH_U_ADDR1_REG 0x208 +#define SUN6I_ISP_MCH_V_ADDR1_REG 0x20c +#define SUN6I_ISP_SCH_Y_ADDR0_REG 0x210 +#define SUN6I_ISP_SCH_U_ADDR0_REG 0x214 +#define SUN6I_ISP_SCH_V_ADDR0_REG 0x218 +#define SUN6I_ISP_SCH_Y_ADDR1_REG 0x21c +#define SUN6I_ISP_SCH_U_ADDR1_REG 0x220 +#define SUN6I_ISP_SCH_V_ADDR1_REG 0x224 + +#endif diff --git a/drivers/staging/media/sunxi/sun6i-isp/uapi/sun6i-isp-config.h b/drivers/staging/media/sunxi/sun6i-isp/uapi/sun6i-isp-config.h new file mode 100644 index 000000000000..19c24c5fd322 --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/uapi/sun6i-isp-config.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR MIT) */ +/* + * Allwinner A31 ISP Configuration + */ + +#ifndef _UAPI_SUN6I_ISP_CONFIG_H +#define _UAPI_SUN6I_ISP_CONFIG_H + +#include <linux/types.h> + +#define V4L2_META_FMT_SUN6I_ISP_PARAMS v4l2_fourcc('S', '6', 'I', 'P') /* Allwinner A31 ISP Parameters */ + +#define SUN6I_ISP_MODULE_BAYER (1U << 0) +#define SUN6I_ISP_MODULE_BDNF (1U << 1) + +struct sun6i_isp_params_config_bayer { + __u16 offset_r; + __u16 offset_gr; + __u16 offset_gb; + __u16 offset_b; + + __u16 gain_r; + __u16 gain_gr; + __u16 gain_gb; + __u16 gain_b; +}; + +struct sun6i_isp_params_config_bdnf { + __u8 in_dis_min; + __u8 in_dis_max; + + __u8 coefficients_g[7]; + __u8 coefficients_rb[5]; +}; + +struct sun6i_isp_params_config { + __u32 modules_used; + + struct sun6i_isp_params_config_bayer bayer; + struct sun6i_isp_params_config_bdnf bdnf; +}; + +#endif /* _UAPI_SUN6I_ISP_CONFIG_H */ diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c index b26e44adb2be..426e653bd55d 100644 --- a/drivers/staging/media/tegra-video/csi.c +++ b/drivers/staging/media/tegra-video/csi.c @@ -433,7 +433,7 @@ static int tegra_csi_channel_alloc(struct tegra_csi *csi, for (i = 0; i < chan->numgangports; i++) chan->csi_port_nums[i] = port_num + i * CSI_PORTS_PER_BRICK; - chan->of_node = node; + chan->of_node = of_node_get(node); chan->numpads = num_pads; if (num_pads & 0x2) { chan->pads[0].flags = MEDIA_PAD_FL_SINK; @@ -448,6 +448,7 @@ static int tegra_csi_channel_alloc(struct tegra_csi *csi, chan->mipi = tegra_mipi_request(csi->dev, node); if (IS_ERR(chan->mipi)) { ret = PTR_ERR(chan->mipi); + chan->mipi = NULL; dev_err(csi->dev, "failed to get mipi device: %d\n", ret); } @@ -640,6 +641,7 @@ static void tegra_csi_channels_cleanup(struct tegra_csi *csi) media_entity_cleanup(&subdev->entity); } + of_node_put(chan->of_node); list_del(&chan->list); kfree(chan); } diff --git a/drivers/staging/media/tegra-video/csi.h b/drivers/staging/media/tegra-video/csi.h index 4ee05a1785cf..6960ea2e3d36 100644 --- a/drivers/staging/media/tegra-video/csi.h +++ b/drivers/staging/media/tegra-video/csi.h @@ -56,7 +56,7 @@ struct tegra_csi; * @framerate: active framerate for TPG * @h_blank: horizontal blanking for TPG active format * @v_blank: vertical blanking for TPG active format - * @mipi: mipi device for corresponding csi channel pads + * @mipi: mipi device for corresponding csi channel pads, or NULL if not applicable (TPG, error) * @pixel_rate: active pixel rate from the sensor on this channel */ struct tegra_csi_channel { diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index 9d46a36cc014..11dd142c98c5 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -1811,7 +1811,7 @@ static int tegra_vi_graph_parse_one(struct tegra_vi_channel *chan, } /* skip entities that are already processed */ - if (remote == dev_fwnode(vi->dev) || + if (device_match_fwnode(vi->dev, remote) || tegra_vi_graph_find_entity(chan, remote)) { fwnode_handle_put(remote); continue; |