diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-09 09:47:22 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-09 09:47:22 -0700 |
commit | ed63b9c873601ca113da5c7b1745e3946493e9f3 (patch) | |
tree | 94e96db2b79a8d123c0645dd64b3830bc4d20bfe /drivers/media/platform | |
parent | 947fbd4ca9fb38f320b076e68cfccab977c5ea01 (diff) | |
parent | f81cbfc4f82a75ca0a2dc181a9c93b88f0e6509d (diff) | |
download | blackbird-op-linux-ed63b9c873601ca113da5c7b1745e3946493e9f3.tar.gz blackbird-op-linux-ed63b9c873601ca113da5c7b1745e3946493e9f3.zip |
Merge tag 'media/v5.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab:
- new Atmel microship ISC driver
- coda has gained support for mpeg2 and mpeg4
- cxusb gained support for analog TV
- rockchip staging driver was split into two separate staging drivers
- added a new staging driver for Allegro DVT video IP core
- added a new staging driver for Amlogic Meson video decoder
- lots of improvements and cleanups
* tag 'media/v5.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (398 commits)
media: allegro: use new v4l2_m2m_ioctl_try_encoder_cmd funcs
media: doc-rst: Fix typos
media: radio-raremono: change devm_k*alloc to k*alloc
media: stv0297: fix frequency range limit
media: rc: Prefer KEY_NUMERIC_* for number buttons on remotes
media: dvb_frontend: split dvb_frontend_handle_ioctl function
media: mceusb: disable "nonsensical irdata" messages
media: rc: remove redundant dev_err message
media: cec-notifier: add new notifier functions
media: cec: add struct cec_connector_info support
media: cec-notifier: rename variables, check kstrdup and n->conn_name
media: MAINTAINERS: Add maintainers for Media Controller
media: staging: media: tegra-vde: Defer dmabuf's unmapping
media: staging: media: tegra-vde: Add IOMMU support
media: hdpvr: fix locking and a missing msleep
media: v4l2: Test type instead of cfg->type in v4l2_ctrl_new_custom()
media: atmel: atmel-isc: fix i386 build error
media: v4l2-ctrl: Move compound control initialization
media: hantro: Use vb2_get_buffer
media: pci: cx88: Change the type of 'missed' to u64
...
Diffstat (limited to 'drivers/media/platform')
127 files changed, 3191 insertions, 1927 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index f2b5f27ebacb..8a19654b393a 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -6,7 +6,6 @@ menuconfig V4L_PLATFORM_DRIVERS bool "V4L platform devices" depends on MEDIA_CAMERA_SUPPORT - default n help Say Y here to enable support for platform-specific V4L drivers. @@ -155,7 +154,6 @@ config VIDEO_TI_CAL depends on SOC_DRA7XX || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select V4L2_FWNODE - default n help Support for the TI CAL (Camera Adaptation Layer) block found on DRA72X SoC. @@ -168,7 +166,6 @@ menuconfig V4L_MEM2MEM_DRIVERS bool "Memory-to-memory multimedia devices" depends on VIDEO_V4L2 depends on MEDIA_CAMERA_SUPPORT - default n help Say Y here to enable selecting drivers for V4L devices that use system memory for both source and destination buffers, as opposed @@ -236,7 +233,6 @@ config VIDEO_MEDIATEK_MDP select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV select VIDEO_MEDIATEK_VPU - default n help It is a v4l2 driver and present in Mediatek MT8173 SoCs. The driver supports for scaling and color space conversion. @@ -252,7 +248,6 @@ config VIDEO_MEDIATEK_VCODEC select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV select VIDEO_MEDIATEK_VPU - default n help Mediatek video codec driver provides HW capability to encode and decode in a range of video formats @@ -276,7 +271,6 @@ config VIDEO_SAMSUNG_S5P_G2D depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV - default n help This is a v4l2 driver for Samsung S5P and EXYNOS4 G2D 2d graphics accelerator. @@ -296,7 +290,6 @@ config VIDEO_SAMSUNG_S5P_MFC depends on VIDEO_DEV && VIDEO_V4L2 depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG - default n help MFC 5.1 and 6.x driver for V4L2 @@ -459,7 +452,6 @@ config VIDEO_ROCKCHIP_RGA depends on ARCH_ROCKCHIP || COMPILE_TEST select VIDEOBUF2_DMA_SG select V4L2_MEM2MEM_DEV - default n help This is a v4l2 driver for Rockchip SOC RGA 2d graphics accelerator. Rockchip RGA is a separate 2D raster graphic acceleration unit. @@ -477,7 +469,6 @@ config VIDEO_TI_VPE select VIDEO_TI_VPDMA select VIDEO_TI_SC select VIDEO_TI_CSC - default n help Support for the TI VPE(Video Processing Engine) block found on DRA7XX SoC. @@ -530,7 +521,6 @@ config VIDEO_VIM2M depends on VIDEO_DEV && VIDEO_V4L2 select VIDEOBUF2_VMALLOC select V4L2_MEM2MEM_DEV - default n help This is a virtual test device for the memory-to-memory driver framework. @@ -542,7 +532,6 @@ endif #V4L_TEST_DRIVERS menuconfig DVB_PLATFORM_DRIVERS bool "DVB platform devices" depends on MEDIA_DIGITAL_TV_SUPPORT - default n help Say Y here to enable support for platform-specific Digital TV drivers. @@ -678,7 +667,6 @@ endif #CEC_PLATFORM_DRIVERS menuconfig SDR_PLATFORM_DRIVERS bool "SDR platform devices" depends on MEDIA_SDR_SUPPORT - default n help Say Y here to enable support for platform-specific SDR Drivers. diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c index 8144fe36ad48..f899ac3b4a61 100644 --- a/drivers/media/platform/aspeed-video.c +++ b/drivers/media/platform/aspeed-video.c @@ -187,6 +187,7 @@ enum { VIDEO_STREAMING, VIDEO_FRAME_INPRG, VIDEO_STOPPED, + VIDEO_CLOCKS_ON, }; struct aspeed_video_addr { @@ -440,7 +441,7 @@ static int aspeed_video_start_frame(struct aspeed_video *video) if (!(seq_ctrl & VE_SEQ_CTRL_COMP_BUSY) || !(seq_ctrl & VE_SEQ_CTRL_CAP_BUSY)) { - dev_err(video->dev, "Engine busy; don't start frame\n"); + dev_dbg(video->dev, "Engine busy; don't start frame\n"); return -EBUSY; } @@ -462,8 +463,7 @@ static int aspeed_video_start_frame(struct aspeed_video *video) aspeed_video_write(video, VE_COMP_ADDR, addr); aspeed_video_update(video, VE_INTERRUPT_CTRL, 0, - VE_INTERRUPT_COMP_COMPLETE | - VE_INTERRUPT_CAPTURE_COMPLETE); + VE_INTERRUPT_COMP_COMPLETE); aspeed_video_update(video, VE_SEQ_CTRL, 0, VE_SEQ_CTRL_TRIG_CAPTURE | VE_SEQ_CTRL_TRIG_COMP); @@ -483,19 +483,30 @@ static void aspeed_video_enable_mode_detect(struct aspeed_video *video) static void aspeed_video_off(struct aspeed_video *video) { + if (!test_bit(VIDEO_CLOCKS_ON, &video->flags)) + return; + /* Disable interrupts */ aspeed_video_write(video, VE_INTERRUPT_CTRL, 0); + aspeed_video_write(video, VE_INTERRUPT_STATUS, 0xffffffff); /* Turn off the relevant clocks */ - clk_disable_unprepare(video->vclk); - clk_disable_unprepare(video->eclk); + clk_disable(video->vclk); + clk_disable(video->eclk); + + clear_bit(VIDEO_CLOCKS_ON, &video->flags); } static void aspeed_video_on(struct aspeed_video *video) { + if (test_bit(VIDEO_CLOCKS_ON, &video->flags)) + return; + /* Turn on the relevant clocks */ - clk_prepare_enable(video->eclk); - clk_prepare_enable(video->vclk); + clk_enable(video->eclk); + clk_enable(video->vclk); + + set_bit(VIDEO_CLOCKS_ON, &video->flags); } static void aspeed_video_bufs_done(struct aspeed_video *video, @@ -511,7 +522,7 @@ static void aspeed_video_bufs_done(struct aspeed_video *video, spin_unlock_irqrestore(&video->lock, flags); } -static void aspeed_video_irq_res_change(struct aspeed_video *video) +static void aspeed_video_irq_res_change(struct aspeed_video *video, ulong delay) { dev_dbg(video->dev, "Resolution changed; resetting\n"); @@ -521,7 +532,7 @@ static void aspeed_video_irq_res_change(struct aspeed_video *video) aspeed_video_off(video); aspeed_video_bufs_done(video, VB2_BUF_STATE_ERROR); - schedule_delayed_work(&video->res_work, RESOLUTION_CHANGE_DELAY); + schedule_delayed_work(&video->res_work, delay); } static irqreturn_t aspeed_video_irq(int irq, void *arg) @@ -534,7 +545,7 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg) * re-initialize */ if (sts & VE_INTERRUPT_MODE_DETECT_WD) { - aspeed_video_irq_res_change(video); + aspeed_video_irq_res_change(video, 0); return IRQ_HANDLED; } @@ -544,7 +555,7 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg) VE_INTERRUPT_MODE_DETECT, 0); aspeed_video_write(video, VE_INTERRUPT_STATUS, VE_INTERRUPT_MODE_DETECT); - + sts &= ~VE_INTERRUPT_MODE_DETECT; set_bit(VIDEO_MODE_DETECT_DONE, &video->flags); wake_up_interruptible_all(&video->wait); } else { @@ -552,13 +563,13 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg) * Signal acquired while NOT doing resolution * detection; reset the engine and re-initialize */ - aspeed_video_irq_res_change(video); + aspeed_video_irq_res_change(video, + RESOLUTION_CHANGE_DELAY); return IRQ_HANDLED; } } - if ((sts & VE_INTERRUPT_COMP_COMPLETE) && - (sts & VE_INTERRUPT_CAPTURE_COMPLETE)) { + if (sts & VE_INTERRUPT_COMP_COMPLETE) { struct aspeed_video_buffer *buf; u32 frame_size = aspeed_video_read(video, VE_OFFSET_COMP_STREAM); @@ -587,17 +598,15 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg) VE_SEQ_CTRL_FORCE_IDLE | VE_SEQ_CTRL_TRIG_COMP, 0); aspeed_video_update(video, VE_INTERRUPT_CTRL, - VE_INTERRUPT_COMP_COMPLETE | - VE_INTERRUPT_CAPTURE_COMPLETE, 0); + VE_INTERRUPT_COMP_COMPLETE, 0); aspeed_video_write(video, VE_INTERRUPT_STATUS, - VE_INTERRUPT_COMP_COMPLETE | - VE_INTERRUPT_CAPTURE_COMPLETE); - + VE_INTERRUPT_COMP_COMPLETE); + sts &= ~VE_INTERRUPT_COMP_COMPLETE; if (test_bit(VIDEO_STREAMING, &video->flags) && buf) aspeed_video_start_frame(video); } - return IRQ_HANDLED; + return sts ? IRQ_NONE : IRQ_HANDLED; } static void aspeed_video_check_and_set_polarity(struct aspeed_video *video) @@ -723,27 +732,6 @@ static void aspeed_video_get_resolution(struct aspeed_video *video) det->height = MIN_HEIGHT; video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; - /* - * Since we need max buffer size for detection, free the second source - * buffer first. - */ - if (video->srcs[1].size) - aspeed_video_free_buf(video, &video->srcs[1]); - - if (video->srcs[0].size < VE_MAX_SRC_BUFFER_SIZE) { - if (video->srcs[0].size) - aspeed_video_free_buf(video, &video->srcs[0]); - - if (!aspeed_video_alloc_buf(video, &video->srcs[0], - VE_MAX_SRC_BUFFER_SIZE)) { - dev_err(video->dev, - "Failed to allocate source buffers\n"); - return; - } - } - - aspeed_video_write(video, VE_SRC0_ADDR, video->srcs[0].dma); - do { if (tries) { set_current_state(TASK_INTERRUPTIBLE); @@ -758,7 +746,7 @@ static void aspeed_video_get_resolution(struct aspeed_video *video) res_check(video), MODE_DETECT_TIMEOUT); if (!rc) { - dev_err(video->dev, "Timed out; first mode detect\n"); + dev_dbg(video->dev, "Timed out; first mode detect\n"); clear_bit(VIDEO_RES_DETECT, &video->flags); return; } @@ -776,7 +764,7 @@ static void aspeed_video_get_resolution(struct aspeed_video *video) MODE_DETECT_TIMEOUT); clear_bit(VIDEO_RES_DETECT, &video->flags); if (!rc) { - dev_err(video->dev, "Timed out; second mode detect\n"); + dev_dbg(video->dev, "Timed out; second mode detect\n"); return; } @@ -810,7 +798,7 @@ static void aspeed_video_get_resolution(struct aspeed_video *video) } while (invalid_resolution && (tries++ < INVALID_RESOLUTION_RETRIES)); if (invalid_resolution) { - dev_err(video->dev, "Invalid resolution detected\n"); + dev_dbg(video->dev, "Invalid resolution detected\n"); return; } @@ -836,8 +824,29 @@ static void aspeed_video_set_resolution(struct aspeed_video *video) struct v4l2_bt_timings *act = &video->active_timings; unsigned int size = act->width * act->height; + /* Set capture/compression frame sizes */ aspeed_video_calc_compressed_size(video, size); + if (video->active_timings.width == 1680) { + /* + * This is a workaround to fix a silicon bug on A1 and A2 + * revisions. Since it doesn't break capturing operation of + * other revisions, use it for all revisions without checking + * the revision ID. It picked 1728 which is a very next + * 64-pixels aligned value to 1680 to minimize memory bandwidth + * and to get better access speed from video engine. + */ + aspeed_video_write(video, VE_CAP_WINDOW, + 1728 << 16 | act->height); + size += (1728 - 1680) * video->active_timings.height; + } else { + aspeed_video_write(video, VE_CAP_WINDOW, + act->width << 16 | act->height); + } + aspeed_video_write(video, VE_COMP_WINDOW, + act->width << 16 | act->height); + aspeed_video_write(video, VE_SRC_SCANLINE_OFFSET, act->width * 4); + /* Don't use direct mode below 1024 x 768 (irqs don't fire) */ if (size < DIRECT_FETCH_THRESHOLD) { aspeed_video_write(video, VE_TGS_0, @@ -854,29 +863,16 @@ static void aspeed_video_set_resolution(struct aspeed_video *video) aspeed_video_update(video, VE_CTRL, 0, VE_CTRL_DIRECT_FETCH); } - /* Set capture/compression frame sizes */ - aspeed_video_write(video, VE_CAP_WINDOW, - act->width << 16 | act->height); - aspeed_video_write(video, VE_COMP_WINDOW, - act->width << 16 | act->height); - aspeed_video_write(video, VE_SRC_SCANLINE_OFFSET, act->width * 4); - size *= 4; - if (size == video->srcs[0].size / 2) { - aspeed_video_write(video, VE_SRC1_ADDR, - video->srcs[0].dma + size); - } else if (size == video->srcs[0].size) { - if (!aspeed_video_alloc_buf(video, &video->srcs[1], size)) - goto err_mem; - - aspeed_video_write(video, VE_SRC1_ADDR, video->srcs[1].dma); - } else { - aspeed_video_free_buf(video, &video->srcs[0]); + if (size != video->srcs[0].size) { + if (video->srcs[0].size) + aspeed_video_free_buf(video, &video->srcs[0]); + if (video->srcs[1].size) + aspeed_video_free_buf(video, &video->srcs[1]); if (!aspeed_video_alloc_buf(video, &video->srcs[0], size)) goto err_mem; - if (!aspeed_video_alloc_buf(video, &video->srcs[1], size)) goto err_mem; @@ -1445,7 +1441,7 @@ static void aspeed_video_stop_streaming(struct vb2_queue *q) !test_bit(VIDEO_FRAME_INPRG, &video->flags), STOP_TIMEOUT); if (!rc) { - dev_err(video->dev, "Timed out when stopping streaming\n"); + dev_dbg(video->dev, "Timed out when stopping streaming\n"); /* * Need to force stop any DMA and try and get HW into a good @@ -1589,8 +1585,8 @@ static int aspeed_video_init(struct aspeed_video *video) return -ENODEV; } - rc = devm_request_irq(dev, irq, aspeed_video_irq, IRQF_SHARED, - DEVICE_NAME, video); + rc = devm_request_threaded_irq(dev, irq, NULL, aspeed_video_irq, + IRQF_ONESHOT, DEVICE_NAME, video); if (rc < 0) { dev_err(dev, "Unable to request IRQ %d\n", irq); return rc; @@ -1602,31 +1598,46 @@ static int aspeed_video_init(struct aspeed_video *video) return PTR_ERR(video->eclk); } + rc = clk_prepare(video->eclk); + if (rc) + return rc; + video->vclk = devm_clk_get(dev, "vclk"); if (IS_ERR(video->vclk)) { dev_err(dev, "Unable to get VCLK\n"); - return PTR_ERR(video->vclk); + rc = PTR_ERR(video->vclk); + goto err_unprepare_eclk; } + rc = clk_prepare(video->vclk); + if (rc) + goto err_unprepare_eclk; + of_reserved_mem_device_init(dev); rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); if (rc) { dev_err(dev, "Failed to set DMA mask\n"); - of_reserved_mem_device_release(dev); - return rc; + goto err_release_reserved_mem; } if (!aspeed_video_alloc_buf(video, &video->jpeg, VE_JPEG_HEADER_SIZE)) { dev_err(dev, "Failed to allocate DMA for JPEG header\n"); - of_reserved_mem_device_release(dev); - return rc; + goto err_release_reserved_mem; } aspeed_video_init_jpeg_table(video->jpeg.virt, video->yuv420); return 0; + +err_release_reserved_mem: + of_reserved_mem_device_release(dev); + clk_unprepare(video->vclk); +err_unprepare_eclk: + clk_unprepare(video->eclk); + + return rc; } static int aspeed_video_probe(struct platform_device *pdev) @@ -1670,6 +1681,11 @@ static int aspeed_video_remove(struct platform_device *pdev) struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); struct aspeed_video *video = to_aspeed_video(v4l2_dev); + aspeed_video_off(video); + + clk_unprepare(video->vclk); + clk_unprepare(video->eclk); + video_unregister_device(&video->vdev); vb2_queue_release(&video->queue); diff --git a/drivers/media/platform/atmel/Makefile b/drivers/media/platform/atmel/Makefile index 484936604ccb..2dba38994a70 100644 --- a/drivers/media/platform/atmel/Makefile +++ b/drivers/media/platform/atmel/Makefile @@ -1,3 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel-isc.o +atmel-isc-objs = atmel-sama5d2-isc.o atmel-isc-base.o + obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o +obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel-isc.o diff --git a/drivers/media/platform/atmel/atmel-isc.c b/drivers/media/platform/atmel/atmel-isc-base.c index 05b9cfb91d20..c1c776b348a9 100644 --- a/drivers/media/platform/atmel/atmel-isc.c +++ b/drivers/media/platform/atmel/atmel-isc-base.c @@ -1,24 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Atmel Image Sensor Controller (ISC) driver + * Microchip Image Sensor Controller (ISC) common driver base * - * Copyright (C) 2016 Atmel + * Copyright (C) 2016-2019 Microchip Technology, Inc. * - * Author: Songjun Wu <songjun.wu@microchip.com> + * 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> @@ -45,176 +33,19 @@ #include <media/videobuf2-dma-contig.h> #include "atmel-isc-regs.h" +#include "atmel-isc.h" -#define ATMEL_ISC_NAME "atmel_isc" - -#define ISC_MAX_SUPPORT_WIDTH 2592 -#define ISC_MAX_SUPPORT_HEIGHT 1944 - -#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; - u8 id; - u8 parent_id; - u32 div; - struct device *dev; -}; - -#define to_isc_clk(hw) container_of(hw, 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 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 WB_ENABLE BIT(0) -#define CFA_ENABLE BIT(1) -#define CC_ENABLE BIT(2) -#define GAM_ENABLE BIT(3) -#define GAM_BENABLE BIT(4) -#define GAM_GENABLE BIT(5) -#define GAM_RENABLE BIT(6) -#define CSC_ENABLE BIT(7) -#define CBC_ENABLE BIT(8) -#define SUB422_ENABLE BIT(9) -#define SUB420_ENABLE BIT(10) - -#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. - * @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; - - 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; - u8 awb; - - u32 r_gain; - u32 b_gain; - - u32 hist_entry[HIST_ENTRIES]; - u32 hist_count[HIST_BAYER]; - u8 hist_id; - u8 hist_stat; -}; - -#define ISC_PIPE_LINE_NODE_NUM 11 - -struct isc_device { - struct regmap *regmap; - struct clk *hclock; - struct clk *ispck; - struct isc_clk isc_clks[2]; - - 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 regmap_field *pipeline[ISC_PIPE_LINE_NODE_NUM]; +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "debug level (0-2)"); - struct isc_subdev_entity *current_subdev; - struct list_head subdev_entities; -}; +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"); /* This is a list of the formats that the ISC can *output* */ -static struct isc_format controller_formats[] = { +const struct isc_format controller_formats[] = { { .fourcc = V4L2_PIX_FMT_ARGB444, }, @@ -245,7 +76,7 @@ static struct isc_format controller_formats[] = { }; /* This is a list of formats that the ISC can receive as *input* */ -static struct isc_format formats_list[] = { +struct isc_format formats_list[] = { { .fourcc = V4L2_PIX_FMT_SBGGR8, .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, @@ -335,11 +166,8 @@ static struct isc_format formats_list[] = { }, }; -#define GAMMA_MAX 2 -#define GAMMA_ENTRIES 64 - /* Gamma table with gamma 1/2.2 */ -static const u32 isc_gamma_table[GAMMA_MAX + 1][GAMMA_ENTRIES] = { +const u32 isc_gamma_table[GAMMA_MAX + 1][GAMMA_ENTRIES] = { /* 0 --> gamma 1/1.8 */ { 0x65, 0x66002F, 0x950025, 0xBB0020, 0xDB001D, 0xF8001A, 0x1130018, 0x12B0017, 0x1420016, 0x1580014, 0x16D0013, 0x1810012, @@ -383,14 +211,39 @@ static const u32 isc_gamma_table[GAMMA_MAX + 1][GAMMA_ENTRIES] = { #define ISC_IS_FORMAT_RAW(mbus_code) \ (((mbus_code) & 0xf000) == 0x3000) -static unsigned int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "debug level (0-2)"); +static inline void isc_update_awb_ctrls(struct isc_device *isc) +{ + struct isc_ctrls *ctrls = &isc->ctrls; -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"); + regmap_write(isc->regmap, ISC_WB_O_RGR, + (ISC_WB_O_ZERO_VAL - (ctrls->offset[ISC_HIS_CFG_MODE_R])) | + ((ISC_WB_O_ZERO_VAL - ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16)); + regmap_write(isc->regmap, ISC_WB_O_BGB, + (ISC_WB_O_ZERO_VAL - (ctrls->offset[ISC_HIS_CFG_MODE_B])) | + ((ISC_WB_O_ZERO_VAL - ctrls->offset[ISC_HIS_CFG_MODE_GB]) << 16)); + 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, the value + * will be substracted from ISC_WB_O_ZERO_VAL to obtain + * 2's complement of a value between 0 and + * ISC_WB_O_ZERO_VAL >> 1 + */ + isc->ctrls.offset[c] = ISC_WB_O_ZERO_VAL; + } +} static int isc_wait_clk_stable(struct clk_hw *hw) { @@ -646,7 +499,7 @@ static int isc_clk_register(struct isc_device *isc, unsigned int id) return 0; } -static int isc_clk_init(struct isc_device *isc) +int isc_clk_init(struct isc_device *isc) { unsigned int i; int ret; @@ -663,7 +516,7 @@ static int isc_clk_init(struct isc_device *isc) return 0; } -static void isc_clk_cleanup(struct isc_device *isc) +void isc_clk_cleanup(struct isc_device *isc) { unsigned int i; @@ -772,7 +625,9 @@ static void isc_start_dma(struct isc_device *isc) dctrl_dview = isc->config.dctrl_dview; regmap_write(regmap, ISC_DCTRL, 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) @@ -794,11 +649,11 @@ static void isc_set_pipeline(struct isc_device *isc, u32 pipeline) bay_cfg = isc->config.sd_format->cfa_baycfg; + if (ctrls->awb == ISC_WB_NONE) + isc_reset_awb_ctrls(isc); + regmap_write(regmap, ISC_WB_CFG, bay_cfg); - regmap_write(regmap, ISC_WB_O_RGR, 0x0); - regmap_write(regmap, ISC_WB_O_BGR, 0x0); - regmap_write(regmap, ISC_WB_G_RGR, ctrls->r_gain | (0x1 << 25)); - regmap_write(regmap, ISC_WB_G_BGR, ctrls->b_gain | (0x1 << 25)); + isc_update_awb_ctrls(isc); regmap_write(regmap, ISC_CFA_CFG, bay_cfg | ISC_CFA_CFG_EITPOL); @@ -848,13 +703,13 @@ static void isc_set_histogram(struct isc_device *isc, bool enable) if (enable) { regmap_write(regmap, ISC_HIS_CFG, - ISC_HIS_CFG_MODE_R | + 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_HIS_CTRL_EN); regmap_write(regmap, ISC_INTEN, ISC_INT_HISDONE); - ctrls->hist_id = ISC_HIS_CFG_MODE_R; + ctrls->hist_id = ISC_HIS_CFG_MODE_GR; isc_update_profile(isc); regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ); @@ -897,7 +752,7 @@ static int isc_configure(struct isc_device *isc) isc_set_pipeline(isc, pipeline); /* - * The current implemented histogram is available for RAW R, B, GB + * 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 && @@ -949,6 +804,10 @@ static int isc_start_streaming(struct vb2_queue *vq, unsigned int count) 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: @@ -973,6 +832,8 @@ static void isc_stop_streaming(struct vb2_queue *vq) struct isc_buffer *buf; int ret; + v4l2_ctrl_activate(isc->do_wb_ctrl, false); + isc->stop = true; /* Wait until the end of the current frame */ @@ -1433,7 +1294,7 @@ static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f, ret = v4l2_subdev_call(isc->current_subdev->sd, pad, set_fmt, &pad_cfg, &format); if (ret < 0) - goto isc_try_fmt_err; + goto isc_try_fmt_subdev_err; v4l2_fill_pix_format(pixfmt, &format.format); @@ -1448,6 +1309,7 @@ static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f, 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; @@ -1472,6 +1334,12 @@ static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f) return ret; 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); + } /* make the try configuration active */ isc->config = isc->try_config; @@ -1588,7 +1456,7 @@ static int isc_enum_frameintervals(struct file *file, void *fh, .which = V4L2_SUBDEV_FORMAT_ACTIVE, }; int ret = -EINVAL; - int i; + unsigned int i; for (i = 0; i < isc->num_user_formats; i++) if (isc->user_formats[i]->fourcc == fival->pixel_format) @@ -1708,7 +1576,7 @@ static const struct v4l2_file_operations isc_fops = { .poll = vb2_fop_poll, }; -static irqreturn_t isc_interrupt(int irq, void *dev_id) +irqreturn_t isc_interrupt(int irq, void *dev_id) { struct isc_device *isc = (struct isc_device *)dev_id; struct regmap *regmap = isc->regmap; @@ -1755,7 +1623,7 @@ static irqreturn_t isc_interrupt(int irq, void *dev_id) return ret; } -static void isc_hist_count(struct isc_device *isc) +static void isc_hist_count(struct isc_device *isc, u32 *min, u32 *max) { struct regmap *regmap = isc->regmap; struct isc_ctrls *ctrls = &isc->ctrls; @@ -1763,25 +1631,99 @@ static void isc_hist_count(struct isc_device *isc) u32 *hist_entry = &ctrls->hist_entry[0]; u32 i; + *min = 0; + *max = HIST_ENTRIES; + regmap_bulk_read(regmap, ISC_HIS_ENTRY, hist_entry, HIST_ENTRIES); *hist_count = 0; - for (i = 0; i < HIST_ENTRIES; i++) + /* + * 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; } static void isc_wb_update(struct isc_ctrls *ctrls) { u32 *hist_count = &ctrls->hist_count[0]; - u64 g_count = (u64)hist_count[ISC_HIS_CFG_MODE_GB] << 9; - u32 hist_r = hist_count[ISC_HIS_CFG_MODE_R]; - u32 hist_b = hist_count[ISC_HIS_CFG_MODE_B]; + 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; - if (hist_r) - ctrls->r_gain = div_u64(g_count, hist_r); + /* Green histogram is null, nothing to do */ + if (!avg) + return; - if (hist_b) - ctrls->b_gain = div_u64(g_count, hist_b); + 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 */ + if (!ctrls->offset[c]) + ctrls->offset[c] = ISC_WB_O_ZERO_VAL; + + /* + * 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; + + /* multiply both gains and adjust for decimals */ + ctrls->gain[c] = s_gain[c] * gw_gain[c]; + ctrls->gain[c] >>= 9; + } } static void isc_awb_work(struct work_struct *w) @@ -1792,27 +1734,66 @@ static void isc_awb_work(struct work_struct *w) struct isc_ctrls *ctrls = &isc->ctrls; u32 hist_id = ctrls->hist_id; u32 baysel; + unsigned long flags; + u32 min, max; + + /* streaming is not active anymore */ + if (isc->stop) + return; if (ctrls->hist_stat != HIST_ENABLED) return; - isc_hist_count(isc); + isc_hist_count(isc, &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_R; + hist_id = ISC_HIS_CFG_MODE_GR; } ctrls->hist_id = hist_id; baysel = isc->config.sd_format->cfa_baycfg << ISC_HIS_CFG_BAYSEL_SHIFT; + /* if no more auto white balance, reset controls. */ + if (ctrls->awb == ISC_WB_NONE) + isc_reset_awb_ctrls(isc); + pm_runtime_get_sync(isc->dev); + /* + * 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"); + ctrls->awb = ISC_WB_NONE; + } + } regmap_write(regmap, ISC_HIS_CFG, hist_id | baysel | ISC_HIS_CFG_RAR); isc_update_profile(isc); - regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ); + /* 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); } @@ -1823,6 +1804,9 @@ static int isc_s_ctrl(struct v4l2_ctrl *ctrl) 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; @@ -1834,11 +1818,33 @@ static int isc_s_ctrl(struct v4l2_ctrl *ctrl) ctrls->gamma_index = ctrl->val; break; case V4L2_CID_AUTO_WHITE_BALANCE: - ctrls->awb = ctrl->val; - if (ctrls->hist_stat != HIST_ENABLED) { - ctrls->r_gain = 0x1 << 9; - ctrls->b_gain = 0x1 << 9; - } + if (ctrl->val == 1) + ctrls->awb = ISC_WB_AUTO; + else + ctrls->awb = ISC_WB_NONE; + + /* we did not configure ISC yet */ + if (!isc->config.sd_format) + break; + + if (ctrls->hist_stat != HIST_ENABLED) + isc_reset_awb_ctrls(isc); + + if (isc->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); + + break; + case V4L2_CID_DO_WHITE_BALANCE: + /* if AWB is enabled, do nothing */ + if (ctrls->awb == ISC_WB_AUTO) + return 0; + + ctrls->awb = ISC_WB_ONETIME; + isc_set_histogram(isc, true); + v4l2_dbg(1, debug, &isc->v4l2_dev, + "One time white-balance started.\n"); break; default: return -EINVAL; @@ -1859,16 +1865,32 @@ static int isc_ctrl_init(struct isc_device *isc) int ret; ctrls->hist_stat = HIST_INIT; + isc_reset_awb_ctrls(isc); - ret = v4l2_ctrl_handler_init(hdl, 4); + ret = v4l2_ctrl_handler_init(hdl, 5); if (ret < 0) return ret; + ctrls->brightness = 0; + ctrls->contrast = 256; + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -1024, 1023, 1, 0); v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 256); v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, GAMMA_MAX, 1, 2); v4l2_ctrl_new_std(hdl, 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, 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); + v4l2_ctrl_handler_setup(hdl); return 0; @@ -1994,7 +2016,7 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier) struct isc_device, v4l2_dev); struct video_device *vdev = &isc->video_dev; struct vb2_queue *q = &isc->vb2_vidq; - int ret; + int ret = 0; INIT_WORK(&isc->awb_work, isc_awb_work); @@ -2025,30 +2047,31 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier) if (ret < 0) { v4l2_err(&isc->v4l2_dev, "vb2_queue_init() failed: %d\n", ret); - return 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); - return 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"); - return ret; + goto isc_async_complete_err; } ret = isc_ctrl_init(isc); if (ret) { v4l2_err(&isc->v4l2_dev, "Init isc ctrols failed: %d\n", ret); - return ret; + goto isc_async_complete_err; } /* Register video device */ @@ -2068,19 +2091,23 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier) if (ret < 0) { v4l2_err(&isc->v4l2_dev, "video_register_device failed: %d\n", ret); - return ret; + goto isc_async_complete_err; } return 0; + +isc_async_complete_err: + mutex_destroy(&isc->lock); + return ret; } -static const struct v4l2_async_notifier_operations isc_async_ops = { +const struct v4l2_async_notifier_operations isc_async_ops = { .bound = isc_async_bound, .unbind = isc_async_unbind, .complete = isc_async_complete, }; -static void isc_subdev_cleanup(struct isc_device *isc) +void isc_subdev_cleanup(struct isc_device *isc) { struct isc_subdev_entity *subdev_entity; @@ -2092,7 +2119,7 @@ static void isc_subdev_cleanup(struct isc_device *isc) INIT_LIST_HEAD(&isc->subdev_entities); } -static int isc_pipeline_init(struct isc_device *isc) +int isc_pipeline_init(struct isc_device *isc) { struct device *dev = isc->dev; struct regmap *regmap = isc->regmap; @@ -2125,300 +2152,12 @@ static int isc_pipeline_init(struct isc_device *isc) return 0; } -static int isc_parse_dt(struct device *dev, struct isc_device *isc) -{ - struct device_node *np = dev->of_node; - struct device_node *epn = NULL, *rem; - 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; - - rem = of_graph_get_remote_port_parent(epn); - if (!rem) { - dev_notice(dev, "Remote device at %pOF not found\n", - epn); - continue; - } - - ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn), - &v4l2_epn); - if (ret) { - of_node_put(rem); - 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) { - of_node_put(rem); - ret = -ENOMEM; - break; - } - - /* asd will be freed by the subsystem once it's added to the - * notifier list - */ - subdev_entity->asd = kzalloc(sizeof(*subdev_entity->asd), - GFP_KERNEL); - if (!subdev_entity->asd) { - of_node_put(rem); - ret = -ENOMEM; - break; - } - - 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; - - subdev_entity->asd->match_type = V4L2_ASYNC_MATCH_FWNODE; - subdev_entity->asd->match.fwnode = - of_fwnode_handle(rem); - list_add_tail(&subdev_entity->list, &isc->subdev_entities); - } - - of_node_put(epn); - return ret; -} - /* regmap configuration */ #define ATMEL_ISC_REG_MAX 0xbfc -static const struct regmap_config isc_regmap_config = { +const struct regmap_config isc_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, .max_register = ATMEL_ISC_REG_MAX, }; -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; - - 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, &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) { - ret = irq; - dev_err(dev, "failed to get irq: %d\n", ret); - return ret; - } - - ret = devm_request_irq(dev, irq, isc_interrupt, 0, - ATMEL_ISC_NAME, isc); - if (ret < 0) { - dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n", - irq, ret); - return ret; - } - - ret = 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 = isc_clk_init(isc); - if (ret) { - dev_err(dev, "failed to init isc clock: %d\n", ret); - goto unprepare_hclk; - } - - 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 unprepare_hclk; - } - - /* 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; - } - - 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) { - v4l2_async_notifier_init(&subdev_entity->notifier); - - ret = v4l2_async_notifier_add_subdev(&subdev_entity->notifier, - subdev_entity->asd); - if (ret) { - fwnode_handle_put(subdev_entity->asd->match.fwnode); - kfree(subdev_entity->asd); - goto cleanup_subdev; - } - - subdev_entity->notifier.ops = &isc_async_ops; - - ret = v4l2_async_notifier_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); - - return 0; - -cleanup_subdev: - isc_subdev_cleanup(isc); - -unregister_v4l2_device: - v4l2_device_unregister(&isc->v4l2_dev); - -unprepare_clk: - clk_disable_unprepare(isc->ispck); -unprepare_hclk: - clk_disable_unprepare(isc->hclock); - - 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); - clk_disable_unprepare(isc->ispck); - clk_disable_unprepare(isc->hclock); - - isc_subdev_cleanup(isc); - - v4l2_device_unregister(&isc->v4l2_dev); - - 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; - - return clk_prepare_enable(isc->ispck); -} - -static const struct dev_pm_ops atmel_isc_dev_pm_ops = { - SET_RUNTIME_PM_OPS(isc_runtime_suspend, isc_runtime_resume, NULL) -}; - -static const struct of_device_id atmel_isc_of_match[] = { - { .compatible = "atmel,sama5d2-isc" }, - { } -}; -MODULE_DEVICE_TABLE(of, atmel_isc_of_match); - -static struct platform_driver atmel_isc_driver = { - .probe = atmel_isc_probe, - .remove = atmel_isc_remove, - .driver = { - .name = ATMEL_ISC_NAME, - .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 <songjun.wu@microchip.com>"); -MODULE_DESCRIPTION("The V4L2 driver for Atmel-ISC"); -MODULE_LICENSE("GPL v2"); -MODULE_SUPPORTED_DEVICE("video"); diff --git a/drivers/media/platform/atmel/atmel-isc-regs.h b/drivers/media/platform/atmel/atmel-isc-regs.h index 8f7f8efc71a7..c1283fb21bf6 100644 --- a/drivers/media/platform/atmel/atmel-isc-regs.h +++ b/drivers/media/platform/atmel/atmel-isc-regs.h @@ -100,13 +100,15 @@ #define ISC_WB_O_RGR 0x00000060 /* ISC White Balance Offset for B, GB Register */ -#define ISC_WB_O_BGR 0x00000064 +#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_BGR 0x0000006c +#define ISC_WB_G_BGB 0x0000006c + +#define ISC_WB_O_ZERO_VAL (1 << 13) /* ISC Color Filter Array Control Register */ #define ISC_CFA_CTRL 0x00000070 diff --git a/drivers/media/platform/atmel/atmel-isc.h b/drivers/media/platform/atmel/atmel-isc.h new file mode 100644 index 000000000000..bfaed2fad2b5 --- /dev/null +++ b/drivers/media/platform/atmel/atmel-isc.h @@ -0,0 +1,245 @@ +/* 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_ + +#define ISC_MAX_SUPPORT_WIDTH 2592 +#define ISC_MAX_SUPPORT_HEIGHT 1944 + +#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 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 WB_ENABLE BIT(0) +#define CFA_ENABLE BIT(1) +#define CC_ENABLE BIT(2) +#define GAM_ENABLE BIT(3) +#define GAM_BENABLE BIT(4) +#define GAM_GENABLE BIT(5) +#define GAM_RENABLE BIT(6) +#define CSC_ENABLE BIT(7) +#define CBC_ENABLE BIT(8) +#define SUB422_ENABLE BIT(9) +#define SUB420_ENABLE BIT(10) + +#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. + * @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; + + 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]; + u32 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 11 + +/* + * 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 + * + * @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_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 + */ +struct isc_device { + struct regmap *regmap; + struct clk *hclock; + struct clk *ispck; + struct isc_clk isc_clks[2]; + + struct device *dev; + struct v4l2_device v4l2_dev; + struct video_device video_dev; + + struct vb2_queue vb2_vidq; + spinlock_t dma_queue_lock; /* serialize access to dma queue */ + 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 v4l2_ctrl *do_wb_ctrl; + struct work_struct awb_work; + + struct mutex lock; /* serialize access to file operations */ + spinlock_t awb_lock; /* serialize access to DMA buffers from awb work queue */ + + struct regmap_field *pipeline[ISC_PIPE_LINE_NODE_NUM]; + + struct isc_subdev_entity *current_subdev; + struct list_head subdev_entities; +}; + +#define GAMMA_MAX 2 +#define GAMMA_ENTRIES 64 + +#define ATMEL_ISC_NAME "atmel-isc" + +extern struct isc_format formats_list[]; +extern const struct isc_format controller_formats[]; +extern const u32 isc_gamma_table[GAMMA_MAX + 1][GAMMA_ENTRIES]; +extern const struct regmap_config isc_regmap_config; +extern const struct v4l2_async_notifier_operations isc_async_ops; + +irqreturn_t isc_interrupt(int irq, void *dev_id); +int isc_pipeline_init(struct isc_device *isc); +int isc_clk_init(struct isc_device *isc); +void isc_subdev_cleanup(struct isc_device *isc); +void isc_clk_cleanup(struct isc_device *isc); + +#endif diff --git a/drivers/media/platform/atmel/atmel-sama5d2-isc.c b/drivers/media/platform/atmel/atmel-sama5d2-isc.c new file mode 100644 index 000000000000..266df14da2d5 --- /dev/null +++ b/drivers/media/platform/atmel/atmel-sama5d2-isc.c @@ -0,0 +1,348 @@ +// 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_MAX_SUPPORT_WIDTH 2592 +#define ISC_MAX_SUPPORT_HEIGHT 1944 + +#define ISC_CLK_MAX_DIV 255 + +static int isc_parse_dt(struct device *dev, struct isc_device *isc) +{ + struct device_node *np = dev->of_node; + struct device_node *epn = NULL, *rem; + 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; + + rem = of_graph_get_remote_port_parent(epn); + if (!rem) { + dev_notice(dev, "Remote device at %pOF not found\n", + epn); + continue; + } + + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn), + &v4l2_epn); + if (ret) { + of_node_put(rem); + 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) { + of_node_put(rem); + ret = -ENOMEM; + break; + } + + /* asd will be freed by the subsystem once it's added to the + * notifier list + */ + subdev_entity->asd = kzalloc(sizeof(*subdev_entity->asd), + GFP_KERNEL); + if (!subdev_entity->asd) { + of_node_put(rem); + ret = -ENOMEM; + break; + } + + 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; + + subdev_entity->asd->match_type = V4L2_ASYNC_MATCH_FWNODE; + subdev_entity->asd->match.fwnode = of_fwnode_handle(rem); + 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; + + 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, &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) { + ret = irq; + dev_err(dev, "failed to get irq: %d\n", ret); + return ret; + } + + ret = devm_request_irq(dev, irq, isc_interrupt, 0, + ATMEL_ISC_NAME, isc); + if (ret < 0) { + dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n", + irq, ret); + return ret; + } + + ret = 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 = isc_clk_init(isc); + if (ret) { + dev_err(dev, "failed to init isc clock: %d\n", ret); + goto unprepare_hclk; + } + + 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 unprepare_hclk; + } + + /* 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; + } + + 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) { + v4l2_async_notifier_init(&subdev_entity->notifier); + + ret = v4l2_async_notifier_add_subdev(&subdev_entity->notifier, + subdev_entity->asd); + if (ret) { + fwnode_handle_put(subdev_entity->asd->match.fwnode); + kfree(subdev_entity->asd); + goto cleanup_subdev; + } + + subdev_entity->notifier.ops = &isc_async_ops; + + ret = v4l2_async_notifier_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); + + return 0; + +cleanup_subdev: + isc_subdev_cleanup(isc); + +unregister_v4l2_device: + v4l2_device_unregister(&isc->v4l2_dev); + +unprepare_clk: + clk_disable_unprepare(isc->ispck); +unprepare_hclk: + clk_disable_unprepare(isc->hclock); + + 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); + + isc_subdev_cleanup(isc); + + v4l2_device_unregister(&isc->v4l2_dev); + + clk_disable_unprepare(isc->ispck); + clk_disable_unprepare(isc->hclock); + + 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) +}; + +static const struct of_device_id atmel_isc_of_match[] = { + { .compatible = "atmel,sama5d2-isc" }, + { } +}; +MODULE_DEVICE_TABLE(of, atmel_isc_of_match); + +static struct platform_driver atmel_isc_driver = { + .probe = atmel_isc_probe, + .remove = atmel_isc_remove, + .driver = { + .name = ATMEL_ISC_NAME, + .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"); +MODULE_SUPPORTED_DEVICE("video"); diff --git a/drivers/media/platform/cec-gpio/cec-gpio.c b/drivers/media/platform/cec-gpio/cec-gpio.c index d2861749d640..5b17d3a31896 100644 --- a/drivers/media/platform/cec-gpio/cec-gpio.c +++ b/drivers/media/platform/cec-gpio/cec-gpio.c @@ -17,7 +17,6 @@ struct cec_gpio { struct gpio_desc *cec_gpio; int cec_irq; bool cec_is_low; - bool cec_have_irq; struct gpio_desc *hpd_gpio; int hpd_irq; @@ -55,9 +54,6 @@ static void cec_gpio_low(struct cec_adapter *adap) if (cec->cec_is_low) return; - if (WARN_ON_ONCE(cec->cec_have_irq)) - free_irq(cec->cec_irq, cec); - cec->cec_have_irq = false; cec->cec_is_low = true; gpiod_set_value(cec->cec_gpio, 0); } @@ -114,14 +110,7 @@ static bool cec_gpio_enable_irq(struct cec_adapter *adap) { struct cec_gpio *cec = cec_get_drvdata(adap); - if (cec->cec_have_irq) - return true; - - if (request_irq(cec->cec_irq, cec_gpio_irq_handler, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - adap->name, cec)) - return false; - cec->cec_have_irq = true; + enable_irq(cec->cec_irq); return true; } @@ -129,9 +118,7 @@ static void cec_gpio_disable_irq(struct cec_adapter *adap) { struct cec_gpio *cec = cec_get_drvdata(adap); - if (cec->cec_have_irq) - free_irq(cec->cec_irq, cec); - cec->cec_have_irq = false; + disable_irq(cec->cec_irq); } static void cec_gpio_status(struct cec_adapter *adap, struct seq_file *file) @@ -139,8 +126,7 @@ static void cec_gpio_status(struct cec_adapter *adap, struct seq_file *file) struct cec_gpio *cec = cec_get_drvdata(adap); seq_printf(file, "mode: %s\n", cec->cec_is_low ? "low-drive" : "read"); - if (cec->cec_have_irq) - seq_printf(file, "using irq: %d\n", cec->cec_irq); + seq_printf(file, "using irq: %d\n", cec->cec_irq); if (cec->hpd_gpio) seq_printf(file, "hpd: %s\n", cec->hpd_is_high ? "high" : "low"); @@ -215,6 +201,14 @@ static int cec_gpio_probe(struct platform_device *pdev) if (IS_ERR(cec->adap)) return PTR_ERR(cec->adap); + ret = devm_request_irq(dev, cec->cec_irq, cec_gpio_irq_handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + cec->adap->name, cec); + if (ret) + return ret; + + cec_gpio_disable_irq(cec->adap); + if (cec->hpd_gpio) { cec->hpd_irq = gpiod_to_irq(cec->hpd_gpio); ret = devm_request_threaded_irq(dev, cec->hpd_irq, diff --git a/drivers/media/platform/coda/Makefile b/drivers/media/platform/coda/Makefile index f13adacd924e..bbb16425a875 100644 --- a/drivers/media/platform/coda/Makefile +++ b/drivers/media/platform/coda/Makefile @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only -ccflags-y += -I$(src) -coda-objs := coda-common.o coda-bit.o coda-gdi.o coda-h264.o coda-jpeg.o +coda-vpu-objs := coda-common.o coda-bit.o coda-gdi.o coda-h264.o coda-mpeg2.o coda-mpeg4.o coda-jpeg.o -obj-$(CONFIG_VIDEO_CODA) += coda.o +obj-$(CONFIG_VIDEO_CODA) += coda-vpu.o obj-$(CONFIG_VIDEO_IMX_VDOA) += imx-vdoa.o diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 976f6aa69f41..00c7bed3dd57 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -98,6 +98,8 @@ static int coda_command_sync(struct coda_ctx *ctx, int cmd) struct coda_dev *dev = ctx->dev; int ret; + lockdep_assert_held(&dev->coda_mutex); + coda_command_async(ctx, cmd); ret = coda_wait_timeout(dev); trace_coda_bit_done(ctx); @@ -112,6 +114,8 @@ int coda_hw_reset(struct coda_ctx *ctx) unsigned int idx; int ret; + lockdep_assert_held(&dev->coda_mutex); + if (!dev->rstc) return -ENOENT; @@ -176,7 +180,7 @@ static void coda_kfifo_sync_to_device_write(struct coda_ctx *ctx) coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); } -static int coda_bitstream_pad(struct coda_ctx *ctx, u32 size) +static int coda_h264_bitstream_pad(struct coda_ctx *ctx, u32 size) { unsigned char *buf; u32 n; @@ -195,51 +199,122 @@ static int coda_bitstream_pad(struct coda_ctx *ctx, u32 size) return (n < size) ? -ENOSPC : 0; } -static int coda_bitstream_queue(struct coda_ctx *ctx, - struct vb2_v4l2_buffer *src_buf) +int coda_bitstream_flush(struct coda_ctx *ctx) { - u32 src_size = vb2_get_plane_payload(&src_buf->vb2_buf, 0); - u32 n; + int ret; - n = kfifo_in(&ctx->bitstream_fifo, - vb2_plane_vaddr(&src_buf->vb2_buf, 0), src_size); - if (n < src_size) - return -ENOSPC; + if (ctx->inst_type != CODA_INST_DECODER || !ctx->use_bit) + return 0; - src_buf->sequence = ctx->qsequence++; + ret = coda_command_sync(ctx, CODA_COMMAND_DEC_BUF_FLUSH); + if (ret < 0) { + v4l2_err(&ctx->dev->v4l2_dev, "failed to flush bitstream\n"); + return ret; + } + + kfifo_init(&ctx->bitstream_fifo, ctx->bitstream.vaddr, + ctx->bitstream.size); + coda_kfifo_sync_to_device_full(ctx); return 0; } +static int coda_bitstream_queue(struct coda_ctx *ctx, const u8 *buf, u32 size) +{ + u32 n = kfifo_in(&ctx->bitstream_fifo, buf, size); + + return (n < size) ? -ENOSPC : 0; +} + +static u32 coda_buffer_parse_headers(struct coda_ctx *ctx, + struct vb2_v4l2_buffer *src_buf, + u32 payload) +{ + u8 *vaddr = vb2_plane_vaddr(&src_buf->vb2_buf, 0); + u32 size = 0; + + switch (ctx->codec->src_fourcc) { + case V4L2_PIX_FMT_MPEG2: + size = coda_mpeg2_parse_headers(ctx, vaddr, payload); + break; + case V4L2_PIX_FMT_MPEG4: + size = coda_mpeg4_parse_headers(ctx, vaddr, payload); + break; + default: + break; + } + + return size; +} + static bool coda_bitstream_try_queue(struct coda_ctx *ctx, struct vb2_v4l2_buffer *src_buf) { unsigned long payload = vb2_get_plane_payload(&src_buf->vb2_buf, 0); + u8 *vaddr = vb2_plane_vaddr(&src_buf->vb2_buf, 0); int ret; + int i; if (coda_get_bitstream_payload(ctx) + payload + 512 >= ctx->bitstream.size) return false; - if (vb2_plane_vaddr(&src_buf->vb2_buf, 0) == NULL) { + if (!vaddr) { v4l2_err(&ctx->dev->v4l2_dev, "trying to queue empty buffer\n"); return true; } - /* Add zero padding before the first H.264 buffer, if it is too small */ + if (ctx->qsequence == 0 && payload < 512) { + /* + * Add padding after the first buffer, if it is too small to be + * fetched by the CODA, by repeating the headers. Without + * repeated headers, or the first frame already queued, decoder + * sequence initialization fails with error code 0x2000 on i.MX6 + * or error code 0x1 on i.MX51. + */ + u32 header_size = coda_buffer_parse_headers(ctx, src_buf, + payload); + + if (header_size) { + coda_dbg(1, ctx, "pad with %u-byte header\n", + header_size); + for (i = payload; i < 512; i += header_size) { + ret = coda_bitstream_queue(ctx, vaddr, + header_size); + if (ret < 0) { + v4l2_err(&ctx->dev->v4l2_dev, + "bitstream buffer overflow\n"); + return false; + } + if (ctx->dev->devtype->product == CODA_960) + break; + } + } else { + coda_dbg(1, ctx, + "could not parse header, sequence initialization might fail\n"); + } + } + + /* Add padding before the first buffer, if it is too small */ if (ctx->qsequence == 0 && payload < 512 && ctx->codec->src_fourcc == V4L2_PIX_FMT_H264) - coda_bitstream_pad(ctx, 512 - payload); + coda_h264_bitstream_pad(ctx, 512 - payload); - ret = coda_bitstream_queue(ctx, src_buf); + ret = coda_bitstream_queue(ctx, vaddr, payload); if (ret < 0) { v4l2_err(&ctx->dev->v4l2_dev, "bitstream buffer overflow\n"); return false; } + + src_buf->sequence = ctx->qsequence++; + /* Sync read pointer to device */ if (ctx == v4l2_m2m_get_curr_priv(ctx->dev->m2m_dev)) coda_kfifo_sync_to_device_write(ctx); + /* Set the stream-end flag after the last buffer is queued */ + if (src_buf->flags & V4L2_BUF_FLAG_LAST) + coda_bit_stream_end_flag(ctx); ctx->hold = false; return true; @@ -327,6 +402,9 @@ void coda_fill_bitstream(struct coda_ctx *ctx, struct list_head *buffer_list) meta->timestamp = src_buf->vb2_buf.timestamp; meta->start = start; meta->end = ctx->bitstream_fifo.kfifo.in; + meta->last = src_buf->flags & V4L2_BUF_FLAG_LAST; + if (meta->last) + coda_dbg(1, ctx, "marking last meta"); spin_lock(&ctx->buffer_meta_lock); list_add_tail(&meta->list, &ctx->buffer_meta_list); @@ -391,7 +469,7 @@ static void coda_free_framebuffers(struct coda_ctx *ctx) int i; for (i = 0; i < CODA_MAX_FRAMEBUFFERS; i++) - coda_free_aux_buf(ctx->dev, &ctx->internal_frames[i]); + coda_free_aux_buf(ctx->dev, &ctx->internal_frames[i].buf); } static int coda_alloc_framebuffers(struct coda_ctx *ctx, @@ -431,7 +509,7 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, coda_free_framebuffers(ctx); return -ENOMEM; } - ret = coda_alloc_context_buf(ctx, &ctx->internal_frames[i], + ret = coda_alloc_context_buf(ctx, &ctx->internal_frames[i].buf, size, name); kfree(name); if (ret < 0) { @@ -445,7 +523,7 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, u32 y, cb, cr, mvcol; /* Start addresses of Y, Cb, Cr planes */ - y = ctx->internal_frames[i].paddr; + y = ctx->internal_frames[i].buf.paddr; cb = y + ysize; cr = y + ysize + ysize/4; mvcol = y + ysize + ysize/4 + ysize/4; @@ -597,6 +675,102 @@ static int coda_encode_header(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, return 0; } +static u32 coda_slice_mode(struct coda_ctx *ctx) +{ + int size, unit; + + switch (ctx->params.slice_mode) { + case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE: + default: + return 0; + case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB: + size = ctx->params.slice_max_mb; + unit = 1; + break; + case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES: + size = ctx->params.slice_max_bits; + unit = 0; + break; + } + + return ((size & CODA_SLICING_SIZE_MASK) << CODA_SLICING_SIZE_OFFSET) | + ((unit & CODA_SLICING_UNIT_MASK) << CODA_SLICING_UNIT_OFFSET) | + ((1 & CODA_SLICING_MODE_MASK) << CODA_SLICING_MODE_OFFSET); +} + +static int coda_enc_param_change(struct coda_ctx *ctx) +{ + struct coda_dev *dev = ctx->dev; + u32 change_enable = 0; + u32 success; + int ret; + + if (ctx->params.gop_size_changed) { + change_enable |= CODA_PARAM_CHANGE_RC_GOP; + coda_write(dev, ctx->params.gop_size, + CODA_CMD_ENC_PARAM_RC_GOP); + ctx->gopcounter = ctx->params.gop_size - 1; + ctx->params.gop_size_changed = false; + } + if (ctx->params.h264_intra_qp_changed) { + coda_dbg(1, ctx, "parameter change: intra Qp %u\n", + ctx->params.h264_intra_qp); + + if (ctx->params.bitrate) { + change_enable |= CODA_PARAM_CHANGE_RC_INTRA_QP; + coda_write(dev, ctx->params.h264_intra_qp, + CODA_CMD_ENC_PARAM_RC_INTRA_QP); + } + ctx->params.h264_intra_qp_changed = false; + } + if (ctx->params.bitrate_changed) { + coda_dbg(1, ctx, "parameter change: bitrate %u kbit/s\n", + ctx->params.bitrate); + change_enable |= CODA_PARAM_CHANGE_RC_BITRATE; + coda_write(dev, ctx->params.bitrate, + CODA_CMD_ENC_PARAM_RC_BITRATE); + ctx->params.bitrate_changed = false; + } + if (ctx->params.framerate_changed) { + coda_dbg(1, ctx, "parameter change: frame rate %u/%u Hz\n", + ctx->params.framerate & 0xffff, + (ctx->params.framerate >> 16) + 1); + change_enable |= CODA_PARAM_CHANGE_RC_FRAME_RATE; + coda_write(dev, ctx->params.framerate, + CODA_CMD_ENC_PARAM_RC_FRAME_RATE); + ctx->params.framerate_changed = false; + } + if (ctx->params.intra_refresh_changed) { + coda_dbg(1, ctx, "parameter change: intra refresh MBs %u\n", + ctx->params.intra_refresh); + change_enable |= CODA_PARAM_CHANGE_INTRA_MB_NUM; + coda_write(dev, ctx->params.intra_refresh, + CODA_CMD_ENC_PARAM_INTRA_MB_NUM); + ctx->params.intra_refresh_changed = false; + } + if (ctx->params.slice_mode_changed) { + change_enable |= CODA_PARAM_CHANGE_SLICE_MODE; + coda_write(dev, coda_slice_mode(ctx), + CODA_CMD_ENC_PARAM_SLICE_MODE); + ctx->params.slice_mode_changed = false; + } + + if (!change_enable) + return 0; + + coda_write(dev, change_enable, CODA_CMD_ENC_PARAM_CHANGE_ENABLE); + + ret = coda_command_sync(ctx, CODA_COMMAND_RC_CHANGE_PARAMETER); + if (ret < 0) + return ret; + + success = coda_read(dev, CODA_RET_ENC_PARAM_CHANGE_SUCCESS); + if (success != 1) + coda_dbg(1, ctx, "parameter change failed: %u\n", success); + + return 0; +} + static phys_addr_t coda_iram_alloc(struct coda_iram_info *iram, size_t size) { phys_addr_t ret; @@ -1035,33 +1209,16 @@ static int coda_start_encoding(struct coda_ctx *ctx) * in JPEG mode */ if (dst_fourcc != V4L2_PIX_FMT_JPEG) { - switch (ctx->params.slice_mode) { - case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE: - value = 0; - break; - case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB: - value = (ctx->params.slice_max_mb & - CODA_SLICING_SIZE_MASK) - << CODA_SLICING_SIZE_OFFSET; - value |= (1 & CODA_SLICING_UNIT_MASK) - << CODA_SLICING_UNIT_OFFSET; - value |= 1 & CODA_SLICING_MODE_MASK; - break; - case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES: - value = (ctx->params.slice_max_bits & - CODA_SLICING_SIZE_MASK) - << CODA_SLICING_SIZE_OFFSET; - value |= (0 & CODA_SLICING_UNIT_MASK) - << CODA_SLICING_UNIT_OFFSET; - value |= 1 & CODA_SLICING_MODE_MASK; - break; - } + value = coda_slice_mode(ctx); coda_write(dev, value, CODA_CMD_ENC_SEQ_SLICE_MODE); value = ctx->params.gop_size; coda_write(dev, value, CODA_CMD_ENC_SEQ_GOP_SIZE); } if (ctx->params.bitrate) { + ctx->params.bitrate_changed = false; + ctx->params.h264_intra_qp_changed = false; + /* Rate control enabled */ value = (ctx->params.bitrate & CODA_RATECONTROL_BITRATE_MASK) << CODA_RATECONTROL_BITRATE_OFFSET; @@ -1198,9 +1355,9 @@ static int coda_start_encoding(struct coda_ctx *ctx) coda9_set_frame_cache(ctx, q_data_src->fourcc); /* FIXME */ - coda_write(dev, ctx->internal_frames[2].paddr, + coda_write(dev, ctx->internal_frames[2].buf.paddr, CODA9_CMD_SET_FRAME_SUBSAMP_A); - coda_write(dev, ctx->internal_frames[3].paddr, + coda_write(dev, ctx->internal_frames[3].buf.paddr, CODA9_CMD_SET_FRAME_SUBSAMP_B); } } @@ -1316,6 +1473,13 @@ static int coda_prepare_encode(struct coda_ctx *ctx) u32 rot_mode = 0; u32 dst_fourcc; u32 reg; + int ret; + + ret = coda_enc_param_change(ctx); + if (ret < 0) { + v4l2_warn(&ctx->dev->v4l2_dev, "parameter change failed: %d\n", + ret); + } src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); @@ -1452,12 +1616,25 @@ static int coda_prepare_encode(struct coda_ctx *ctx) return 0; } +static char coda_frame_type_char(u32 flags) +{ + return (flags & V4L2_BUF_FLAG_KEYFRAME) ? 'I' : + (flags & V4L2_BUF_FLAG_PFRAME) ? 'P' : + (flags & V4L2_BUF_FLAG_BFRAME) ? 'B' : '?'; +} + static void coda_finish_encode(struct coda_ctx *ctx) { struct vb2_v4l2_buffer *src_buf, *dst_buf; struct coda_dev *dev = ctx->dev; u32 wr_ptr, start_ptr; + /* + * Lock to make sure that an encoder stop command running in parallel + * will either already have marked src_buf as last, or it will wake up + * the capture queue after the buffers are returned. + */ + mutex_lock(&ctx->wakeup_mutex); src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); @@ -1483,33 +1660,30 @@ static void coda_finish_encode(struct coda_ctx *ctx) coda_read(dev, CODA_RET_ENC_PIC_SLICE_NUM); coda_read(dev, CODA_RET_ENC_PIC_FLAG); - if (coda_read(dev, CODA_RET_ENC_PIC_TYPE) == 0) { + dst_buf->flags &= ~(V4L2_BUF_FLAG_KEYFRAME | + V4L2_BUF_FLAG_PFRAME | + V4L2_BUF_FLAG_LAST); + if (coda_read(dev, CODA_RET_ENC_PIC_TYPE) == 0) dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; - dst_buf->flags &= ~V4L2_BUF_FLAG_PFRAME; - } else { + else dst_buf->flags |= V4L2_BUF_FLAG_PFRAME; - dst_buf->flags &= ~V4L2_BUF_FLAG_KEYFRAME; - } + dst_buf->flags |= src_buf->flags & V4L2_BUF_FLAG_LAST; - dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; - dst_buf->field = src_buf->field; - dst_buf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - dst_buf->flags |= - src_buf->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - dst_buf->timecode = src_buf->timecode; + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_DONE); + mutex_unlock(&ctx->wakeup_mutex); ctx->gopcounter--; if (ctx->gopcounter < 0) ctx->gopcounter = ctx->params.gop_size - 1; - coda_dbg(1, ctx, "job finished: encoded %c frame (%d)\n", - (dst_buf->flags & V4L2_BUF_FLAG_KEYFRAME) ? 'I' : 'P', - dst_buf->sequence); + coda_dbg(1, ctx, "job finished: encoded %c frame (%d)%s\n", + coda_frame_type_char(dst_buf->flags), dst_buf->sequence, + (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? " (last)" : ""); } static void coda_seq_end_work(struct work_struct *work) @@ -1579,8 +1753,7 @@ static int coda_alloc_bitstream_buffer(struct coda_ctx *ctx, return 0; ctx->bitstream.size = roundup_pow_of_two(q_data->sizeimage * 2); - ctx->bitstream.vaddr = dma_alloc_wc(&ctx->dev->plat_dev->dev, - ctx->bitstream.size, + ctx->bitstream.vaddr = dma_alloc_wc(ctx->dev->dev, ctx->bitstream.size, &ctx->bitstream.paddr, GFP_KERNEL); if (!ctx->bitstream.vaddr) { v4l2_err(&ctx->dev->v4l2_dev, @@ -1598,8 +1771,8 @@ static void coda_free_bitstream_buffer(struct coda_ctx *ctx) if (ctx->bitstream.vaddr == NULL) return; - dma_free_wc(&ctx->dev->plat_dev->dev, ctx->bitstream.size, - ctx->bitstream.vaddr, ctx->bitstream.paddr); + dma_free_wc(ctx->dev->dev, ctx->bitstream.size, ctx->bitstream.vaddr, + ctx->bitstream.paddr); ctx->bitstream.vaddr = NULL; kfifo_init(&ctx->bitstream_fifo, NULL, 0); } @@ -1656,7 +1829,7 @@ static bool coda_reorder_enable(struct coda_ctx *ctx) return profile > V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; } -static int __coda_start_decoding(struct coda_ctx *ctx) +static int __coda_decoder_seq_init(struct coda_ctx *ctx) { struct coda_q_data *q_data_src, *q_data_dst; u32 bitstream_buf, bitstream_size; @@ -1666,6 +1839,8 @@ static int __coda_start_decoding(struct coda_ctx *ctx) u32 val; int ret; + lockdep_assert_held(&dev->coda_mutex); + coda_dbg(1, ctx, "Video Data Order Adapter: %s\n", ctx->use_vdoa ? "Enabled" : "Disabled"); @@ -1677,8 +1852,6 @@ static int __coda_start_decoding(struct coda_ctx *ctx) src_fourcc = q_data_src->fourcc; dst_fourcc = q_data_dst->fourcc; - coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR); - /* Update coda bitstream read and write pointers from kfifo */ coda_kfifo_sync_to_device_full(ctx); @@ -1739,6 +1912,7 @@ static int __coda_start_decoding(struct coda_ctx *ctx) v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n"); return ret; } + ctx->sequence_offset = ~0U; ctx->initialized = 1; /* Update kfifo out pointer from coda bitstream read pointer */ @@ -1804,6 +1978,64 @@ static int __coda_start_decoding(struct coda_ctx *ctx) (top_bottom & 0x3ff); } + if (dev->devtype->product != CODA_DX6) { + u8 profile, level; + + val = coda_read(dev, CODA7_RET_DEC_SEQ_HEADER_REPORT); + profile = val & 0xff; + level = (val >> 8) & 0x7f; + + if (profile || level) + coda_update_profile_level_ctrls(ctx, profile, level); + } + + return 0; +} + +static void coda_dec_seq_init_work(struct work_struct *work) +{ + struct coda_ctx *ctx = container_of(work, + struct coda_ctx, seq_init_work); + struct coda_dev *dev = ctx->dev; + int ret; + + mutex_lock(&ctx->buffer_mutex); + mutex_lock(&dev->coda_mutex); + + if (ctx->initialized == 1) + goto out; + + ret = __coda_decoder_seq_init(ctx); + if (ret < 0) + goto out; + + ctx->initialized = 1; + +out: + mutex_unlock(&dev->coda_mutex); + mutex_unlock(&ctx->buffer_mutex); +} + +static int __coda_start_decoding(struct coda_ctx *ctx) +{ + struct coda_q_data *q_data_src, *q_data_dst; + struct coda_dev *dev = ctx->dev; + u32 src_fourcc, dst_fourcc; + int ret; + + if (!ctx->initialized) { + ret = __coda_decoder_seq_init(ctx); + if (ret < 0) + return ret; + } + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + src_fourcc = q_data_src->fourcc; + dst_fourcc = q_data_dst->fourcc; + + coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR); + ret = coda_alloc_framebuffers(ctx, q_data_dst, src_fourcc); if (ret < 0) { v4l2_err(&dev->v4l2_dev, "failed to allocate framebuffers\n"); @@ -1812,7 +2044,8 @@ static int __coda_start_decoding(struct coda_ctx *ctx) /* Tell the decoder how many frame buffers we allocated. */ coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM); - coda_write(dev, width, CODA_CMD_SET_FRAME_BUF_STRIDE); + coda_write(dev, round_up(q_data_dst->rect.width, 16), + CODA_CMD_SET_FRAME_BUF_STRIDE); if (dev->devtype->product != CODA_DX6) { /* Set secondary AXI IRAM */ @@ -1928,7 +2161,7 @@ static int coda_prepare_decode(struct coda_ctx *ctx) ctx->display_idx < ctx->num_internal_frames) { vdoa_device_run(ctx->vdoa, vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0), - ctx->internal_frames[ctx->display_idx].paddr); + ctx->internal_frames[ctx->display_idx].buf.paddr); } else { if (dev->devtype->product == CODA_960) { /* @@ -2026,6 +2259,7 @@ static void coda_finish_decode(struct coda_ctx *ctx) int width, height; int decoded_idx; int display_idx; + struct coda_internal_frame *decoded_frame = NULL; u32 src_fourcc; int success; u32 err_mb; @@ -2146,12 +2380,19 @@ static void coda_finish_decode(struct coda_ctx *ctx) else if (ctx->display_idx < 0) ctx->hold = true; } else if (decoded_idx == -2) { + if (ctx->display_idx >= 0 && + ctx->display_idx < ctx->num_internal_frames) + ctx->sequence_offset++; /* no frame was decoded, we still return remaining buffers */ } else if (decoded_idx < 0 || decoded_idx >= ctx->num_internal_frames) { v4l2_err(&dev->v4l2_dev, "decoded frame index out of range: %d\n", decoded_idx); } else { - val = coda_read(dev, CODA_RET_DEC_PIC_FRAME_NUM) - 1; + decoded_frame = &ctx->internal_frames[decoded_idx]; + + val = coda_read(dev, CODA_RET_DEC_PIC_FRAME_NUM); + if (ctx->sequence_offset == -1) + ctx->sequence_offset = val; val -= ctx->sequence_offset; spin_lock(&ctx->buffer_meta_lock); if (!list_empty(&ctx->buffer_meta_list)) { @@ -2173,28 +2414,26 @@ static void coda_finish_decode(struct coda_ctx *ctx) val, ctx->sequence_offset, meta->sequence); } - ctx->frame_metas[decoded_idx] = *meta; + decoded_frame->meta = *meta; kfree(meta); } else { spin_unlock(&ctx->buffer_meta_lock); v4l2_err(&dev->v4l2_dev, "empty timestamp list!\n"); - memset(&ctx->frame_metas[decoded_idx], 0, + memset(&decoded_frame->meta, 0, sizeof(struct coda_buffer_meta)); - ctx->frame_metas[decoded_idx].sequence = val; + decoded_frame->meta.sequence = val; + decoded_frame->meta.last = false; ctx->sequence_offset++; } - trace_coda_dec_pic_done(ctx, &ctx->frame_metas[decoded_idx]); + trace_coda_dec_pic_done(ctx, &decoded_frame->meta); val = coda_read(dev, CODA_RET_DEC_PIC_TYPE) & 0x7; - if (val == 0) - ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_KEYFRAME; - else if (val == 1) - ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_PFRAME; - else - ctx->frame_types[decoded_idx] = V4L2_BUF_FLAG_BFRAME; + decoded_frame->type = (val == 0) ? V4L2_BUF_FLAG_KEYFRAME : + (val == 1) ? V4L2_BUF_FLAG_PFRAME : + V4L2_BUF_FLAG_BFRAME; - ctx->frame_errors[decoded_idx] = err_mb; + decoded_frame->error = err_mb; } if (display_idx == -1) { @@ -2214,6 +2453,10 @@ static void coda_finish_decode(struct coda_ctx *ctx) /* If a frame was copied out, return it */ if (ctx->display_idx >= 0 && ctx->display_idx < ctx->num_internal_frames) { + struct coda_internal_frame *ready_frame; + + ready_frame = &ctx->internal_frames[ctx->display_idx]; + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); dst_buf->sequence = ctx->osequence++; @@ -2221,8 +2464,25 @@ static void coda_finish_decode(struct coda_ctx *ctx) dst_buf->flags &= ~(V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME); - dst_buf->flags |= ctx->frame_types[ctx->display_idx]; - meta = &ctx->frame_metas[ctx->display_idx]; + dst_buf->flags |= ready_frame->type; + meta = &ready_frame->meta; + if (meta->last && !coda_reorder_enable(ctx)) { + /* + * If this was the last decoded frame, and reordering + * is disabled, this will be the last display frame. + */ + coda_dbg(1, ctx, "last meta, marking as last frame\n"); + dst_buf->flags |= V4L2_BUF_FLAG_LAST; + } else if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG && + display_idx == -1) { + /* + * If there is no designated presentation frame anymore, + * this frame has to be the last one. + */ + coda_dbg(1, ctx, + "no more frames to return, marking as last frame\n"); + dst_buf->flags |= V4L2_BUF_FLAG_LAST; + } dst_buf->timecode = meta->timecode; dst_buf->vb2_buf.timestamp = meta->timestamp; @@ -2231,18 +2491,39 @@ static void coda_finish_decode(struct coda_ctx *ctx) vb2_set_plane_payload(&dst_buf->vb2_buf, 0, q_data_dst->sizeimage); - if (ctx->frame_errors[ctx->display_idx] || err_vdoa) + if (ready_frame->error || err_vdoa) coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_ERROR); else coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_DONE); - coda_dbg(1, ctx, "job finished: decoded %c frame (%u/%u)\n", - (dst_buf->flags & V4L2_BUF_FLAG_KEYFRAME) ? 'I' : - ((dst_buf->flags & V4L2_BUF_FLAG_PFRAME) ? 'P' : 'B'), - dst_buf->sequence, ctx->qsequence); + if (decoded_frame) { + coda_dbg(1, ctx, "job finished: decoded %c frame %u, returned %c frame %u (%u/%u)%s\n", + coda_frame_type_char(decoded_frame->type), + decoded_frame->meta.sequence, + coda_frame_type_char(dst_buf->flags), + ready_frame->meta.sequence, + dst_buf->sequence, ctx->qsequence, + (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? + " (last)" : ""); + } else { + coda_dbg(1, ctx, "job finished: no frame decoded (%d), returned %c frame %u (%u/%u)%s\n", + decoded_idx, + coda_frame_type_char(dst_buf->flags), + ready_frame->meta.sequence, + dst_buf->sequence, ctx->qsequence, + (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? + " (last)" : ""); + } } else { - coda_dbg(1, ctx, "job finished: no frame decoded (%u/%u)\n", - ctx->osequence, ctx->qsequence); + if (decoded_frame) { + coda_dbg(1, ctx, "job finished: decoded %c frame %u, no frame returned (%d)\n", + coda_frame_type_char(decoded_frame->type), + decoded_frame->meta.sequence, + ctx->display_idx); + } else { + coda_dbg(1, ctx, "job finished: no frame decoded (%d) or returned (%d)\n", + decoded_idx, ctx->display_idx); + } } /* The rotator will copy the current display frame next time */ @@ -2286,6 +2567,7 @@ const struct coda_context_ops coda_bit_decode_ops = { .prepare_run = coda_prepare_decode, .finish_run = coda_finish_decode, .run_timeout = coda_decode_timeout, + .seq_init_work = coda_dec_seq_init_work, .seq_end_work = coda_seq_end_work, .release = coda_bit_release, }; @@ -2297,6 +2579,7 @@ irqreturn_t coda_irq_handler(int irq, void *data) /* read status register to attend the IRQ */ coda_read(dev, CODA_REG_BIT_INT_STATUS); + coda_write(dev, 0, CODA_REG_BIT_INT_REASON); coda_write(dev, CODA_REG_BIT_INT_CLEAR_SET, CODA_REG_BIT_INT_CLEAR); @@ -2304,7 +2587,6 @@ irqreturn_t coda_irq_handler(int irq, void *data) if (ctx == NULL) { v4l2_err(&dev->v4l2_dev, "Instance released before the end of transaction\n"); - mutex_unlock(&dev->coda_mutex); return IRQ_HANDLED; } diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 6238047273f2..01428de2596e 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -74,7 +74,7 @@ MODULE_PARM_DESC(enable_bwb, "Enable BWB unit for decoding, may crash on certain void coda_write(struct coda_dev *dev, u32 data, u32 reg) { - v4l2_dbg(2, coda_debug, &dev->v4l2_dev, + v4l2_dbg(3, coda_debug, &dev->v4l2_dev, "%s: data=0x%x, reg=0x%x\n", __func__, data, reg); writel(data, dev->regs_base + reg); } @@ -84,7 +84,7 @@ unsigned int coda_read(struct coda_dev *dev, u32 reg) u32 data; data = readl(dev->regs_base + reg); - v4l2_dbg(2, coda_debug, &dev->v4l2_dev, + v4l2_dbg(3, coda_debug, &dev->v4l2_dev, "%s: data=0x%x, reg=0x%x\n", __func__, data, reg); return data; } @@ -879,14 +879,25 @@ static int coda_qbuf(struct file *file, void *priv, { struct coda_ctx *ctx = fh_to_ctx(priv); + if (ctx->inst_type == CODA_INST_DECODER && + buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + buf->flags &= ~V4L2_BUF_FLAG_LAST; + return v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf); } -static bool coda_buf_is_end_of_stream(struct coda_ctx *ctx, - struct vb2_v4l2_buffer *buf) +static int coda_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { - return ((ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) && - (buf->sequence == (ctx->qsequence - 1))); + struct coda_ctx *ctx = fh_to_ctx(priv); + int ret; + + ret = v4l2_m2m_dqbuf(file, ctx->fh.m2m_ctx, buf); + + if (ctx->inst_type == CODA_INST_DECODER && + buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + buf->flags &= ~V4L2_BUF_FLAG_LAST; + + return ret; } void coda_m2m_buf_done(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, @@ -896,11 +907,8 @@ void coda_m2m_buf_done(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, .type = V4L2_EVENT_EOS }; - if (coda_buf_is_end_of_stream(ctx, buf)) { - buf->flags |= V4L2_BUF_FLAG_LAST; - + if (buf->flags & V4L2_BUF_FLAG_LAST) v4l2_event_queue_fh(&ctx->fh, &eos_event); - } v4l2_m2m_buf_done(buf, state); } @@ -1001,36 +1009,52 @@ static int coda_try_encoder_cmd(struct file *file, void *fh, if (ctx->inst_type != CODA_INST_ENCODER) return -ENOTTY; - if (ec->cmd != V4L2_ENC_CMD_STOP) - return -EINVAL; + return v4l2_m2m_ioctl_try_encoder_cmd(file, fh, ec); +} - if (ec->flags & V4L2_ENC_CMD_STOP_AT_GOP_END) - return -EINVAL; +static void coda_wake_up_capture_queue(struct coda_ctx *ctx) +{ + struct vb2_queue *dst_vq; - return 0; + coda_dbg(1, ctx, "waking up capture queue\n"); + + dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + dst_vq->last_buffer_dequeued = true; + wake_up(&dst_vq->done_wq); } static int coda_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *ec) { struct coda_ctx *ctx = fh_to_ctx(fh); - struct vb2_queue *dst_vq; + struct vb2_v4l2_buffer *buf; int ret; ret = coda_try_encoder_cmd(file, fh, ec); if (ret < 0) return ret; - /* Set the stream-end flag on this context */ - ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; + mutex_lock(&ctx->wakeup_mutex); + buf = v4l2_m2m_last_src_buf(ctx->fh.m2m_ctx); + if (buf) { + /* + * If the last output buffer is still on the queue, make sure + * that decoder finish_run will see the last flag and report it + * to userspace. + */ + buf->flags |= V4L2_BUF_FLAG_LAST; + } else { + /* Set the stream-end flag on this context */ + ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; - /* If there is no buffer in flight, wake up */ - if (!ctx->streamon_out || ctx->qsequence == ctx->osequence) { - dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_BUF_TYPE_VIDEO_CAPTURE); - dst_vq->last_buffer_dequeued = true; - wake_up(&dst_vq->done_wq); + /* + * If the last output buffer has already been taken from the + * queue, wake up the capture queue and signal end of stream + * via the -EPIPE mechanism. + */ + coda_wake_up_capture_queue(ctx); } + mutex_unlock(&ctx->wakeup_mutex); return 0; } @@ -1043,32 +1067,89 @@ static int coda_try_decoder_cmd(struct file *file, void *fh, if (ctx->inst_type != CODA_INST_DECODER) return -ENOTTY; - if (dc->cmd != V4L2_DEC_CMD_STOP) - return -EINVAL; - - if (dc->flags & V4L2_DEC_CMD_STOP_TO_BLACK) - return -EINVAL; - - if (!(dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) && (dc->stop.pts != 0)) - return -EINVAL; - - return 0; + return v4l2_m2m_ioctl_try_decoder_cmd(file, fh, dc); } static int coda_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dc) { struct coda_ctx *ctx = fh_to_ctx(fh); + struct coda_dev *dev = ctx->dev; + struct vb2_v4l2_buffer *buf; + struct vb2_queue *dst_vq; + bool stream_end; + bool wakeup; int ret; ret = coda_try_decoder_cmd(file, fh, dc); if (ret < 0) return ret; - /* Set the stream-end flag on this context */ - coda_bit_stream_end_flag(ctx); - ctx->hold = false; - v4l2_m2m_try_schedule(ctx->fh.m2m_ctx); + switch (dc->cmd) { + case V4L2_DEC_CMD_START: + mutex_lock(&ctx->bitstream_mutex); + mutex_lock(&dev->coda_mutex); + coda_bitstream_flush(ctx); + mutex_unlock(&dev->coda_mutex); + dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + vb2_clear_last_buffer_dequeued(dst_vq); + ctx->bit_stream_param &= ~CODA_BIT_STREAM_END_FLAG; + coda_fill_bitstream(ctx, NULL); + mutex_unlock(&ctx->bitstream_mutex); + break; + case V4L2_DEC_CMD_STOP: + stream_end = false; + wakeup = false; + + buf = v4l2_m2m_last_src_buf(ctx->fh.m2m_ctx); + if (buf) { + coda_dbg(1, ctx, "marking last pending buffer\n"); + + /* Mark last buffer */ + buf->flags |= V4L2_BUF_FLAG_LAST; + + if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) == 0) { + coda_dbg(1, ctx, "all remaining buffers queued\n"); + stream_end = true; + } + } else { + coda_dbg(1, ctx, "marking last meta\n"); + + /* Mark last meta */ + spin_lock(&ctx->buffer_meta_lock); + if (!list_empty(&ctx->buffer_meta_list)) { + struct coda_buffer_meta *meta; + + meta = list_last_entry(&ctx->buffer_meta_list, + struct coda_buffer_meta, + list); + meta->last = true; + stream_end = true; + } else { + wakeup = true; + } + spin_unlock(&ctx->buffer_meta_lock); + } + + if (stream_end) { + coda_dbg(1, ctx, "all remaining buffers queued\n"); + + /* Set the stream-end flag on this context */ + coda_bit_stream_end_flag(ctx); + ctx->hold = false; + v4l2_m2m_try_schedule(ctx->fh.m2m_ctx); + } + + if (wakeup) { + /* If there is no buffer in flight, wake up */ + coda_wake_up_capture_queue(ctx); + } + + break; + default: + return -EINVAL; + } return 0; } @@ -1236,6 +1317,7 @@ static int coda_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) tpf = &a->parm.output.timeperframe; coda_approximate_timeperframe(tpf); ctx->params.framerate = coda_timeperframe_to_frate(tpf); + ctx->params.framerate_changed = true; return 0; } @@ -1243,9 +1325,16 @@ static int coda_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) static int coda_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) { + struct coda_ctx *ctx = fh_to_ctx(fh); + switch (sub->type) { case V4L2_EVENT_EOS: return v4l2_event_subscribe(fh, sub, 0, NULL); + case V4L2_EVENT_SOURCE_CHANGE: + if (ctx->inst_type == CODA_INST_DECODER) + return v4l2_event_subscribe(fh, sub, 0, NULL); + else + return -EINVAL; default: return v4l2_ctrl_subscribe_event(fh, sub); } @@ -1269,7 +1358,7 @@ static const struct v4l2_ioctl_ops coda_ioctl_ops = { .vidioc_qbuf = coda_qbuf, .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, - .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_dqbuf = coda_dqbuf, .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, @@ -1325,7 +1414,7 @@ static void coda_pic_run_work(struct work_struct *work) if (!wait_for_completion_timeout(&ctx->completion, msecs_to_jiffies(1000))) { - dev_err(&dev->plat_dev->dev, "CODA PIC_RUN timeout\n"); + dev_err(dev->dev, "CODA PIC_RUN timeout\n"); ctx->hold = true; @@ -1412,7 +1501,7 @@ static int coda_job_ready(void *m2m_priv) return 0; } - coda_dbg(1, ctx, "job ready\n"); + coda_dbg(2, ctx, "job ready\n"); return 1; } @@ -1563,42 +1652,81 @@ static void coda_update_menu_ctrl(struct v4l2_ctrl *ctrl, int value) v4l2_ctrl_unlock(ctrl); } -static void coda_update_h264_profile_ctrl(struct coda_ctx *ctx) +void coda_update_profile_level_ctrls(struct coda_ctx *ctx, u8 profile_idc, + u8 level_idc) { const char * const *profile_names; + const char * const *level_names; + struct v4l2_ctrl *profile_ctrl; + struct v4l2_ctrl *level_ctrl; + const char *codec_name; + u32 profile_cid; + u32 level_cid; int profile; + int level; - profile = coda_h264_profile(ctx->params.h264_profile_idc); - if (profile < 0) { - v4l2_warn(&ctx->dev->v4l2_dev, "Invalid H264 Profile: %u\n", - ctx->params.h264_profile_idc); + switch (ctx->codec->src_fourcc) { + case V4L2_PIX_FMT_H264: + codec_name = "H264"; + profile_cid = V4L2_CID_MPEG_VIDEO_H264_PROFILE; + level_cid = V4L2_CID_MPEG_VIDEO_H264_LEVEL; + profile_ctrl = ctx->h264_profile_ctrl; + level_ctrl = ctx->h264_level_ctrl; + profile = coda_h264_profile(profile_idc); + level = coda_h264_level(level_idc); + break; + case V4L2_PIX_FMT_MPEG2: + codec_name = "MPEG-2"; + profile_cid = V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE; + level_cid = V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL; + profile_ctrl = ctx->mpeg2_profile_ctrl; + level_ctrl = ctx->mpeg2_level_ctrl; + profile = coda_mpeg2_profile(profile_idc); + level = coda_mpeg2_level(level_idc); + break; + case V4L2_PIX_FMT_MPEG4: + codec_name = "MPEG-4"; + profile_cid = V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE; + level_cid = V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL; + profile_ctrl = ctx->mpeg4_profile_ctrl; + level_ctrl = ctx->mpeg4_level_ctrl; + profile = coda_mpeg4_profile(profile_idc); + level = coda_mpeg4_level(level_idc); + break; + default: return; } - coda_update_menu_ctrl(ctx->h264_profile_ctrl, profile); - - profile_names = v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_PROFILE); - - coda_dbg(1, ctx, "Parsed H264 Profile: %s\n", profile_names[profile]); -} + profile_names = v4l2_ctrl_get_menu(profile_cid); + level_names = v4l2_ctrl_get_menu(level_cid); -static void coda_update_h264_level_ctrl(struct coda_ctx *ctx) -{ - const char * const *level_names; - int level; + if (profile < 0) { + v4l2_warn(&ctx->dev->v4l2_dev, "Invalid %s profile: %u\n", + codec_name, profile_idc); + } else { + coda_dbg(1, ctx, "Parsed %s profile: %s\n", codec_name, + profile_names[profile]); + coda_update_menu_ctrl(profile_ctrl, profile); + } - level = coda_h264_level(ctx->params.h264_level_idc); if (level < 0) { - v4l2_warn(&ctx->dev->v4l2_dev, "Invalid H264 Level: %u\n", - ctx->params.h264_level_idc); - return; + v4l2_warn(&ctx->dev->v4l2_dev, "Invalid %s level: %u\n", + codec_name, level_idc); + } else { + coda_dbg(1, ctx, "Parsed %s level: %s\n", codec_name, + level_names[level]); + coda_update_menu_ctrl(level_ctrl, level); } +} - coda_update_menu_ctrl(ctx->h264_level_ctrl, level); - - level_names = v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_LEVEL); +static void coda_queue_source_change_event(struct coda_ctx *ctx) +{ + static const struct v4l2_event source_change_event = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, + }; - coda_dbg(1, ctx, "Parsed H264 Level: %s\n", level_names[level]); + v4l2_event_queue_fh(&ctx->fh, &source_change_event); } static void coda_buf_queue(struct vb2_buffer *vb) @@ -1631,8 +1759,9 @@ static void coda_buf_queue(struct vb2_buffer *vb) */ if (!ctx->params.h264_profile_idc) { coda_sps_parse_profile(ctx, vb); - coda_update_h264_profile_ctrl(ctx); - coda_update_h264_level_ctrl(ctx); + coda_update_profile_level_ctrls(ctx, + ctx->params.h264_profile_idc, + ctx->params.h264_level_idc); } } @@ -1642,6 +1771,22 @@ static void coda_buf_queue(struct vb2_buffer *vb) /* This set buf->sequence = ctx->qsequence++ */ coda_fill_bitstream(ctx, NULL); mutex_unlock(&ctx->bitstream_mutex); + + if (!ctx->initialized) { + /* + * Run sequence initialization in case the queued + * buffer contained headers. + */ + if (vb2_is_streaming(vb->vb2_queue) && + ctx->ops->seq_init_work) { + queue_work(ctx->dev->workqueue, + &ctx->seq_init_work); + flush_work(&ctx->seq_init_work); + } + + if (ctx->initialized) + coda_queue_source_change_event(ctx); + } } else { if (ctx->inst_type == CODA_INST_ENCODER && vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) @@ -1653,7 +1798,7 @@ static void coda_buf_queue(struct vb2_buffer *vb) int coda_alloc_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf, size_t size, const char *name, struct dentry *parent) { - buf->vaddr = dma_alloc_coherent(&dev->plat_dev->dev, size, &buf->paddr, + buf->vaddr = dma_alloc_coherent(dev->dev, size, &buf->paddr, GFP_KERNEL); if (!buf->vaddr) { v4l2_err(&dev->v4l2_dev, @@ -1670,7 +1815,7 @@ int coda_alloc_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf, buf->dentry = debugfs_create_blob(name, 0644, parent, &buf->blob); if (!buf->dentry) - dev_warn(&dev->plat_dev->dev, + dev_warn(dev->dev, "failed to create debugfs entry %s\n", name); } @@ -1681,8 +1826,7 @@ void coda_free_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf) { if (buf->vaddr) { - dma_free_coherent(&dev->plat_dev->dev, buf->size, - buf->vaddr, buf->paddr); + dma_free_coherent(dev->dev, buf->size, buf->vaddr, buf->paddr); buf->vaddr = NULL; buf->size = 0; debugfs_remove(buf->dentry); @@ -1715,10 +1859,21 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) coda_fill_bitstream(ctx, &list); mutex_unlock(&ctx->bitstream_mutex); - if (coda_get_bitstream_payload(ctx) < 512) { + if (ctx->dev->devtype->product != CODA_960 && + coda_get_bitstream_payload(ctx) < 512) { + v4l2_err(v4l2_dev, "start payload < 512\n"); ret = -EINVAL; goto err; } + + if (!ctx->initialized) { + /* Run sequence initialization */ + if (ctx->ops->seq_init_work) { + queue_work(ctx->dev->workqueue, + &ctx->seq_init_work); + flush_work(&ctx->seq_init_work); + } + } } ctx->streamon_out = 1; @@ -1853,11 +2008,16 @@ static const struct vb2_ops coda_qops = { static int coda_s_ctrl(struct v4l2_ctrl *ctrl) { + const char * const *val_names = v4l2_ctrl_get_menu(ctrl->id); struct coda_ctx *ctx = container_of(ctrl->handler, struct coda_ctx, ctrls); - coda_dbg(1, ctx, "s_ctrl: id = 0x%x, name = \"%s\", val = %d\n", - ctrl->id, ctrl->name, ctrl->val); + if (val_names) + coda_dbg(2, ctx, "s_ctrl: id = 0x%x, name = \"%s\", val = %d (\"%s\")\n", + ctrl->id, ctrl->name, ctrl->val, val_names[ctrl->val]); + else + coda_dbg(2, ctx, "s_ctrl: id = 0x%x, name = \"%s\", val = %d\n", + ctrl->id, ctrl->name, ctrl->val); switch (ctrl->id) { case V4L2_CID_HFLIP: @@ -1874,12 +2034,14 @@ static int coda_s_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_MPEG_VIDEO_BITRATE: ctx->params.bitrate = ctrl->val / 1000; + ctx->params.bitrate_changed = true; break; case V4L2_CID_MPEG_VIDEO_GOP_SIZE: ctx->params.gop_size = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: ctx->params.h264_intra_qp = ctrl->val; + ctx->params.h264_intra_qp_changed = true; break; case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: ctx->params.h264_inter_qp = ctrl->val; @@ -1919,23 +2081,29 @@ static int coda_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: ctx->params.mpeg4_inter_qp = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE: + case V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL: case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: /* nothing to do, these are fixed */ break; case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: ctx->params.slice_mode = ctrl->val; + ctx->params.slice_mode_changed = true; break; case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: ctx->params.slice_max_mb = ctrl->val; + ctx->params.slice_mode_changed = true; break; case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES: ctx->params.slice_max_bits = ctrl->val * 8; + ctx->params.slice_mode_changed = true; break; case V4L2_CID_MPEG_VIDEO_HEADER_MODE: break; case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: ctx->params.intra_refresh = ctrl->val; + ctx->params.intra_refresh_changed = true; break; case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: ctx->params.force_ipicture = true; @@ -2040,7 +2208,7 @@ static void coda_encode_ctrls(struct coda_ctx *ctx) } v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, - V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES, 0x0, + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES, 0x0, V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE); v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, 1, 0x3fffffff, 1, 1); @@ -2098,6 +2266,34 @@ static void coda_decode_ctrls(struct coda_ctx *ctx) &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_LEVEL, max, 0, max); if (ctx->h264_level_ctrl) ctx->h264_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctx->mpeg2_profile_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE, + V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH, 0, + V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH); + if (ctx->mpeg2_profile_ctrl) + ctx->mpeg2_profile_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctx->mpeg2_level_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL, + V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH, 0, + V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH); + if (ctx->mpeg2_level_ctrl) + ctx->mpeg2_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctx->mpeg4_profile_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, + V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY, 0, + V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY); + if (ctx->mpeg4_profile_ctrl) + ctx->mpeg4_profile_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctx->mpeg4_level_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, + V4L2_MPEG_VIDEO_MPEG4_LEVEL_5, 0, + V4L2_MPEG_VIDEO_MPEG4_LEVEL_5); + if (ctx->mpeg4_level_ctrl) + ctx->mpeg4_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; } static int coda_ctrls_setup(struct coda_ctx *ctx) @@ -2154,7 +2350,7 @@ static int coda_queue_init(struct coda_ctx *ctx, struct vb2_queue *vq) * queues to have at least one buffer queued. */ vq->min_buffers_needed = 1; - vq->dev = &ctx->dev->plat_dev->dev; + vq->dev = ctx->dev->dev; return vb2_queue_init(vq); } @@ -2240,6 +2436,8 @@ static int coda_open(struct file *file) ctx->use_bit = !ctx->cvd->direct; init_completion(&ctx->completion); INIT_WORK(&ctx->pic_run_work, coda_pic_run_work); + if (ctx->ops->seq_init_work) + INIT_WORK(&ctx->seq_init_work, ctx->ops->seq_init_work); if (ctx->ops->seq_end_work) INIT_WORK(&ctx->seq_end_work, ctx->ops->seq_end_work); v4l2_fh_init(&ctx->fh, video_devdata(file)); @@ -2277,7 +2475,7 @@ static int coda_open(struct file *file) ctx->use_vdoa = false; /* Power up and upload firmware if necessary */ - ret = pm_runtime_get_sync(&dev->plat_dev->dev); + ret = pm_runtime_get_sync(dev->dev); if (ret < 0) { v4l2_err(&dev->v4l2_dev, "failed to power up: %d\n", ret); goto err_pm_get; @@ -2312,6 +2510,7 @@ static int coda_open(struct file *file) mutex_init(&ctx->bitstream_mutex); mutex_init(&ctx->buffer_mutex); + mutex_init(&ctx->wakeup_mutex); INIT_LIST_HEAD(&ctx->buffer_meta_list); spin_lock_init(&ctx->buffer_meta_lock); @@ -2324,7 +2523,7 @@ err_ctx_init: err_clk_ahb: clk_disable_unprepare(dev->clk_per); err_clk_per: - pm_runtime_put_sync(&dev->plat_dev->dev); + pm_runtime_put_sync(dev->dev); err_pm_get: v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); @@ -2363,7 +2562,7 @@ static int coda_release(struct file *file) v4l2_ctrl_handler_free(&ctx->ctrls); clk_disable_unprepare(dev->clk_ahb); clk_disable_unprepare(dev->clk_per); - pm_runtime_put_sync(&dev->plat_dev->dev); + pm_runtime_put_sync(dev->dev); v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); ida_free(&dev->ida, ctx->idx); @@ -2486,9 +2685,12 @@ err_clk_per: static int coda_register_device(struct coda_dev *dev, int i) { struct video_device *vfd = &dev->vfd[i]; + enum coda_inst_type type; + int ret; if (i >= dev->devtype->num_vdevs) return -EINVAL; + type = dev->devtype->vdevs[i]->type; strscpy(vfd->name, dev->devtype->vdevs[i]->name, sizeof(vfd->name)); vfd->fops = &coda_fops; @@ -2504,7 +2706,12 @@ static int coda_register_device(struct coda_dev *dev, int i) v4l2_disable_ioctl(vfd, VIDIOC_G_CROP); v4l2_disable_ioctl(vfd, VIDIOC_S_CROP); - return video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + if (!ret) + v4l2_info(&dev->v4l2_dev, "%s registered as %s\n", + type == CODA_INST_ENCODER ? "encoder" : "decoder", + video_device_node_name(vfd)); + return ret; } static void coda_copy_firmware(struct coda_dev *dev, const u8 * const buf, @@ -2550,18 +2757,16 @@ static int coda_firmware_request(struct coda_dev *dev) fw = dev->devtype->firmware[dev->firmware]; - dev_dbg(&dev->plat_dev->dev, "requesting firmware '%s' for %s\n", fw, + dev_dbg(dev->dev, "requesting firmware '%s' for %s\n", fw, coda_product_name(dev->devtype->product)); - return request_firmware_nowait(THIS_MODULE, true, fw, - &dev->plat_dev->dev, GFP_KERNEL, dev, - coda_fw_callback); + return request_firmware_nowait(THIS_MODULE, true, fw, dev->dev, + GFP_KERNEL, dev, coda_fw_callback); } static void coda_fw_callback(const struct firmware *fw, void *context) { struct coda_dev *dev = context; - struct platform_device *pdev = dev->plat_dev; int i, ret; if (!fw) { @@ -2579,7 +2784,7 @@ static void coda_fw_callback(const struct firmware *fw, void *context) * firmware requests, report that the fallback firmware was * found. */ - dev_info(&pdev->dev, "Using fallback firmware %s\n", + dev_info(dev->dev, "Using fallback firmware %s\n", dev->devtype->firmware[dev->firmware]); } @@ -2618,10 +2823,7 @@ static void coda_fw_callback(const struct firmware *fw, void *context) } } - v4l2_info(&dev->v4l2_dev, "codec registered as /dev/video[%d-%d]\n", - dev->vfd[0].num, dev->vfd[i - 1].num); - - pm_runtime_put_sync(&pdev->dev); + pm_runtime_put_sync(dev->dev); return; rel_vfd: @@ -2629,7 +2831,7 @@ rel_vfd: video_unregister_device(&dev->vfd[i]); v4l2_m2m_release(dev->m2m_dev); put_pm: - pm_runtime_put_sync(&pdev->dev); + pm_runtime_put_sync(dev->dev); } enum coda_platform { @@ -2744,7 +2946,6 @@ static int coda_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct gen_pool *pool; struct coda_dev *dev; - struct resource *res; int ret, irq; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); @@ -2762,7 +2963,7 @@ static int coda_probe(struct platform_device *pdev) spin_lock_init(&dev->irqlock); - dev->plat_dev = pdev; + dev->dev = &pdev->dev; dev->clk_per = devm_clk_get(&pdev->dev, "per"); if (IS_ERR(dev->clk_per)) { dev_err(&pdev->dev, "Could not get per clock\n"); @@ -2776,8 +2977,7 @@ static int coda_probe(struct platform_device *pdev) } /* Get memory for physical registers */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dev->regs_base = devm_ioremap_resource(&pdev->dev, res); + dev->regs_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(dev->regs_base)) return PTR_ERR(dev->regs_base); @@ -2790,8 +2990,8 @@ static int coda_probe(struct platform_device *pdev) return irq; } - ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, coda_irq_handler, - IRQF_ONESHOT, dev_name(&pdev->dev), dev); + ret = devm_request_irq(&pdev->dev, irq, coda_irq_handler, 0, + dev_name(&pdev->dev), dev); if (ret < 0) { dev_err(&pdev->dev, "failed to request irq: %d\n", ret); return ret; diff --git a/drivers/media/platform/coda/coda-h264.c b/drivers/media/platform/coda/coda-h264.c index a2fa29da1d31..8bd0aa8af114 100644 --- a/drivers/media/platform/coda/coda-h264.c +++ b/drivers/media/platform/coda/coda-h264.c @@ -10,7 +10,8 @@ #include <linux/kernel.h> #include <linux/string.h> #include <linux/videodev2.h> -#include <coda.h> + +#include "coda.h" static const u8 coda_filler_size[8] = { 0, 7, 14, 13, 12, 11, 10, 9 }; diff --git a/drivers/media/platform/coda/coda-mpeg2.c b/drivers/media/platform/coda/coda-mpeg2.c new file mode 100644 index 000000000000..6f3f6721d286 --- /dev/null +++ b/drivers/media/platform/coda/coda-mpeg2.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Coda multi-standard codec IP - MPEG-2 helper functions + * + * Copyright (C) 2019 Pengutronix, Philipp Zabel + */ + +#include <linux/kernel.h> +#include <linux/videodev2.h> +#include "coda.h" + +int coda_mpeg2_profile(int profile_idc) +{ + switch (profile_idc) { + case 5: + return V4L2_MPEG_VIDEO_MPEG2_PROFILE_SIMPLE; + case 4: + return V4L2_MPEG_VIDEO_MPEG2_PROFILE_MAIN; + case 3: + return V4L2_MPEG_VIDEO_MPEG2_PROFILE_SNR_SCALABLE; + case 2: + return V4L2_MPEG_VIDEO_MPEG2_PROFILE_SPATIALLY_SCALABLE; + case 1: + return V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH; + default: + return -EINVAL; + } +} + +int coda_mpeg2_level(int level_idc) +{ + switch (level_idc) { + case 10: + return V4L2_MPEG_VIDEO_MPEG2_LEVEL_LOW; + case 8: + return V4L2_MPEG_VIDEO_MPEG2_LEVEL_MAIN; + case 6: + return V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH_1440; + case 4: + return V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH; + default: + return -EINVAL; + } +} + +/* + * Check if the buffer starts with the MPEG-2 sequence header (with or without + * quantization matrix) and extension header, for example: + * + * 00 00 01 b3 2d 01 e0 34 08 8b a3 81 + * 10 11 11 12 12 12 13 13 13 13 14 14 14 14 14 15 + * 15 15 15 15 15 16 16 16 16 16 16 16 17 17 17 17 + * 17 17 17 17 18 18 18 19 18 18 18 19 1a 1a 1a 1a + * 19 1b 1b 1b 1b 1b 1c 1c 1c 1c 1e 1e 1e 1f 1f 21 + * 00 00 01 b5 14 8a 00 01 00 00 + * + * or: + * + * 00 00 01 b3 08 00 40 15 ff ff e0 28 + * 00 00 01 b5 14 8a 00 01 00 00 + * + * Returns the detected header size in bytes or 0. + */ +u32 coda_mpeg2_parse_headers(struct coda_ctx *ctx, u8 *buf, u32 size) +{ + static const u8 sequence_header_start[4] = { 0x00, 0x00, 0x01, 0xb3 }; + static const union { + u8 extension_start[4]; + u8 start_code_prefix[3]; + } u = { { 0x00, 0x00, 0x01, 0xb5 } }; + + if (size < 22 || + memcmp(buf, sequence_header_start, 4) != 0) + return 0; + + if ((size == 22 || + (size >= 25 && memcmp(buf + 22, u.start_code_prefix, 3) == 0)) && + memcmp(buf + 12, u.extension_start, 4) == 0) + return 22; + + if ((size == 86 || + (size > 89 && memcmp(buf + 86, u.start_code_prefix, 3) == 0)) && + memcmp(buf + 76, u.extension_start, 4) == 0) + return 86; + + return 0; +} diff --git a/drivers/media/platform/coda/coda-mpeg4.c b/drivers/media/platform/coda/coda-mpeg4.c new file mode 100644 index 000000000000..483a4fba1b4f --- /dev/null +++ b/drivers/media/platform/coda/coda-mpeg4.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Coda multi-standard codec IP - MPEG-4 helper functions + * + * Copyright (C) 2019 Pengutronix, Philipp Zabel + */ + +#include <linux/kernel.h> +#include <linux/videodev2.h> + +#include "coda.h" + +int coda_mpeg4_profile(int profile_idc) +{ + switch (profile_idc) { + case 0: + return V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE; + case 15: + return V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE; + case 2: + return V4L2_MPEG_VIDEO_MPEG4_PROFILE_CORE; + case 1: + return V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE_SCALABLE; + case 11: + return V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY; + default: + return -EINVAL; + } +} + +int coda_mpeg4_level(int level_idc) +{ + switch (level_idc) { + case 0: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_0; + case 1: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_1; + case 2: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_2; + case 3: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_3; + case 4: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_4; + case 5: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_5; + default: + return -EINVAL; + } +} + +/* + * Check if the buffer starts with the MPEG-4 visual object sequence and visual + * object headers, for example: + * + * 00 00 01 b0 f1 + * 00 00 01 b5 a9 13 00 00 01 00 00 00 01 20 08 + * d4 8d 88 00 f5 04 04 08 14 30 3f + * + * Returns the detected header size in bytes or 0. + */ +u32 coda_mpeg4_parse_headers(struct coda_ctx *ctx, u8 *buf, u32 size) +{ + static const u8 vos_start[4] = { 0x00, 0x00, 0x01, 0xb0 }; + static const union { + u8 vo_start[4]; + u8 start_code_prefix[3]; + } u = { { 0x00, 0x00, 0x01, 0xb5 } }; + + if (size < 30 || + memcmp(buf, vos_start, 4) != 0 || + memcmp(buf + 5, u.vo_start, 4) != 0) + return 0; + + if (size == 30 || + (size >= 33 && memcmp(buf + 30, u.start_code_prefix, 3) == 0)) + return 30; + + if (size == 31 || + (size >= 34 && memcmp(buf + 31, u.start_code_prefix, 3) == 0)) + return 31; + + if (size == 32 || + (size >= 35 && memcmp(buf + 32, u.start_code_prefix, 3) == 0)) + return 32; + + return 0; +} diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index cfcfff7838cd..848bf1da401e 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -70,7 +70,7 @@ struct coda_aux_buf { struct coda_dev { struct v4l2_device v4l2_dev; struct video_device vfd[5]; - struct platform_device *plat_dev; + struct device *dev; const struct coda_devtype *devtype; int firmware; struct vdoa_data *vdoa; @@ -118,6 +118,8 @@ struct coda_params { s8 h264_chroma_qp_index_offset; u8 h264_profile_idc; u8 h264_level_idc; + u8 mpeg2_profile_idc; + u8 mpeg2_level_idc; u8 mpeg4_intra_qp; u8 mpeg4_inter_qp; u8 gop_size; @@ -135,6 +137,12 @@ struct coda_params { u32 slice_max_bits; u32 slice_max_mb; bool force_ipicture; + bool gop_size_changed; + bool bitrate_changed; + bool framerate_changed; + bool h264_intra_qp_changed; + bool intra_refresh_changed; + bool slice_mode_changed; }; struct coda_buffer_meta { @@ -144,6 +152,7 @@ struct coda_buffer_meta { u64 timestamp; unsigned int start; unsigned int end; + bool last; }; /* Per-queue, driver-specific private data */ @@ -183,14 +192,23 @@ struct coda_context_ops { int (*prepare_run)(struct coda_ctx *ctx); void (*finish_run)(struct coda_ctx *ctx); void (*run_timeout)(struct coda_ctx *ctx); + void (*seq_init_work)(struct work_struct *work); void (*seq_end_work)(struct work_struct *work); void (*release)(struct coda_ctx *ctx); }; +struct coda_internal_frame { + struct coda_aux_buf buf; + struct coda_buffer_meta meta; + u32 type; + u32 error; +}; + struct coda_ctx { struct coda_dev *dev; struct mutex buffer_mutex; struct work_struct pic_run_work; + struct work_struct seq_init_work; struct work_struct seq_end_work; struct completion completion; const struct coda_video_device *cvd; @@ -213,6 +231,10 @@ struct coda_ctx { struct v4l2_ctrl_handler ctrls; struct v4l2_ctrl *h264_profile_ctrl; struct v4l2_ctrl *h264_level_ctrl; + struct v4l2_ctrl *mpeg2_profile_ctrl; + struct v4l2_ctrl *mpeg2_level_ctrl; + struct v4l2_ctrl *mpeg4_profile_ctrl; + struct v4l2_ctrl *mpeg4_level_ctrl; struct v4l2_fh fh; int gopcounter; int runcounter; @@ -225,10 +247,7 @@ struct coda_ctx { struct coda_aux_buf parabuf; struct coda_aux_buf psbuf; struct coda_aux_buf slicebuf; - struct coda_aux_buf internal_frames[CODA_MAX_FRAMEBUFFERS]; - u32 frame_types[CODA_MAX_FRAMEBUFFERS]; - struct coda_buffer_meta frame_metas[CODA_MAX_FRAMEBUFFERS]; - u32 frame_errors[CODA_MAX_FRAMEBUFFERS]; + struct coda_internal_frame internal_frames[CODA_MAX_FRAMEBUFFERS]; struct list_head buffer_meta_list; spinlock_t buffer_meta_lock; int num_metas; @@ -241,11 +260,18 @@ struct coda_ctx { u32 bit_stream_param; u32 frm_dis_flg; u32 frame_mem_ctrl; + u32 para_change; int display_idx; struct dentry *debugfs_entry; bool use_bit; bool use_vdoa; struct vdoa_ctx *vdoa; + /* + * wakeup mutex used to serialize encoder stop command and finish_run, + * ensures that finish_run always either flags the last returned buffer + * or wakes up the capture queue to signal EOS afterwards. + */ + struct mutex wakeup_mutex; }; extern int coda_debug; @@ -310,6 +336,7 @@ static inline bool coda_bitstream_can_fetch_past(struct coda_ctx *ctx, } bool coda_bitstream_can_fetch_past(struct coda_ctx *ctx, unsigned int pos); +int coda_bitstream_flush(struct coda_ctx *ctx); void coda_bit_stream_end_flag(struct coda_ctx *ctx); @@ -324,6 +351,16 @@ int coda_sps_parse_profile(struct coda_ctx *ctx, struct vb2_buffer *vb); int coda_h264_sps_fixup(struct coda_ctx *ctx, int width, int height, char *buf, int *size, int max_size); +int coda_mpeg2_profile(int profile_idc); +int coda_mpeg2_level(int level_idc); +u32 coda_mpeg2_parse_headers(struct coda_ctx *ctx, u8 *buf, u32 size); +int coda_mpeg4_profile(int profile_idc); +int coda_mpeg4_level(int level_idc); +u32 coda_mpeg4_parse_headers(struct coda_ctx *ctx, u8 *buf, u32 size); + +void coda_update_profile_level_ctrls(struct coda_ctx *ctx, u8 profile_idc, + u8 level_idc); + bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_buffer *vb); int coda_jpeg_write_tables(struct coda_ctx *ctx); void coda_set_jpeg_compression_quality(struct coda_ctx *ctx, int quality); diff --git a/drivers/media/platform/coda/coda_regs.h b/drivers/media/platform/coda/coda_regs.h index abf8e195f6c0..b17464b56d3d 100644 --- a/drivers/media/platform/coda/coda_regs.h +++ b/drivers/media/platform/coda/coda_regs.h @@ -177,7 +177,7 @@ #define CODA_RET_DEC_SEQ_FRATE_DR 0x1e8 #define CODA_RET_DEC_SEQ_JPG_PARA 0x1e4 #define CODA_RET_DEC_SEQ_JPG_THUMB_IND 0x1e8 -#define CODA9_RET_DEC_SEQ_HEADER_REPORT 0x1ec +#define CODA7_RET_DEC_SEQ_HEADER_REPORT 0x1ec /* Decoder Picture Run */ #define CODA_CMD_DEC_PIC_ROT_MODE 0x180 @@ -342,6 +342,24 @@ #define CODA_CMD_ENC_SEQ_JPG_THUMB_SIZE 0x1a4 #define CODA_CMD_ENC_SEQ_JPG_THUMB_OFFSET 0x1a8 +/* Encoder Parameter Change */ +#define CODA_CMD_ENC_PARAM_CHANGE_ENABLE 0x180 +#define CODA_PARAM_CHANGE_RC_GOP BIT(0) +#define CODA_PARAM_CHANGE_RC_INTRA_QP BIT(1) +#define CODA_PARAM_CHANGE_RC_BITRATE BIT(2) +#define CODA_PARAM_CHANGE_RC_FRAME_RATE BIT(3) +#define CODA_PARAM_CHANGE_INTRA_MB_NUM BIT(4) +#define CODA_PARAM_CHANGE_SLICE_MODE BIT(5) +#define CODA_PARAM_CHANGE_HEC_MODE BIT(6) +#define CODA_CMD_ENC_PARAM_RC_GOP 0x184 +#define CODA_CMD_ENC_PARAM_RC_INTRA_QP 0x188 +#define CODA_CMD_ENC_PARAM_RC_BITRATE 0x18c +#define CODA_CMD_ENC_PARAM_RC_FRAME_RATE 0x190 +#define CODA_CMD_ENC_PARAM_INTRA_MB_NUM 0x194 +#define CODA_CMD_ENC_PARAM_SLICE_MODE 0x198 +#define CODA_CMD_ENC_PARAM_HEC_MODE 0x19c +#define CODA_RET_ENC_PARAM_CHANGE_SUCCESS 0x1c0 + /* Encoder Picture Run */ #define CODA9_CMD_ENC_PIC_SRC_INDEX 0x180 #define CODA9_CMD_ENC_PIC_SRC_STRIDE 0x184 diff --git a/drivers/media/platform/coda/trace.h b/drivers/media/platform/coda/trace.h index a672bfc4c6ba..6cf58237fff2 100644 --- a/drivers/media/platform/coda/trace.h +++ b/drivers/media/platform/coda/trace.h @@ -157,7 +157,7 @@ DEFINE_EVENT(coda_buf_meta_class, coda_dec_rot_done, #endif /* __CODA_TRACE_H__ */ #undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_PATH ../../drivers/media/platform/coda #undef TRACE_INCLUDE_FILE #define TRACE_INCLUDE_FILE trace diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 61809d2050fa..f0f7ef638c56 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -1376,6 +1376,14 @@ vpif_init_free_channel_objects: return err; } +static inline void free_vpif_objs(void) +{ + int i; + + for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) + kfree(vpif_obj.dev[i]); +} + static int vpif_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) @@ -1645,7 +1653,7 @@ static __init int vpif_probe(struct platform_device *pdev) err = v4l2_device_register(vpif_dev, &vpif_obj.v4l2_dev); if (err) { v4l2_err(vpif_dev->driver, "Error registering v4l2 device\n"); - goto cleanup; + goto vpif_free; } while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, res_idx))) { @@ -1692,7 +1700,9 @@ static __init int vpif_probe(struct platform_device *pdev) "registered sub device %s\n", subdevdata->name); } - vpif_probe_complete(); + err = vpif_probe_complete(); + if (err) + goto probe_subdev_out; } else { vpif_obj.notifier.ops = &vpif_async_ops; err = v4l2_async_notifier_register(&vpif_obj.v4l2_dev, @@ -1711,6 +1721,8 @@ probe_subdev_out: kfree(vpif_obj.sd); vpif_unregister: v4l2_device_unregister(&vpif_obj.v4l2_dev); +vpif_free: + free_vpif_objs(); cleanup: v4l2_async_notifier_cleanup(&vpif_obj.notifier); diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/davinci/vpss.c index 3f079ac1b080..d38d2bbb6f0f 100644 --- a/drivers/media/platform/davinci/vpss.c +++ b/drivers/media/platform/davinci/vpss.c @@ -498,9 +498,9 @@ static struct platform_driver vpss_driver = { static void vpss_exit(void) { + platform_driver_unregister(&vpss_driver); iounmap(oper_cfg.vpss_regs_base2); release_mem_region(VPSS_CLK_CTRL, 4); - platform_driver_unregister(&vpss_driver); } static int __init vpss_init(void) @@ -509,6 +509,11 @@ static int __init vpss_init(void) return -EBUSY; oper_cfg.vpss_regs_base2 = ioremap(VPSS_CLK_CTRL, 4); + if (unlikely(!oper_cfg.vpss_regs_base2)) { + release_mem_region(VPSS_CLK_CTRL, 4); + return -ENOMEM; + } + writel(VPSS_CLK_CTRL_VENCCLKEN | VPSS_CLK_CTRL_DACCLKEN, oper_cfg.vpss_regs_base2); diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index ea46d7387221..854869f0024e 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -327,7 +327,7 @@ void gsc_check_src_scale_info(struct gsc_variant *var, } } -int gsc_enum_fmt_mplane(struct v4l2_fmtdesc *f) +int gsc_enum_fmt(struct v4l2_fmtdesc *f) { const struct gsc_fmt *fmt; diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h index 3ada9737c8f7..772183b090c2 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.h +++ b/drivers/media/platform/exynos-gsc/gsc-core.h @@ -385,7 +385,7 @@ void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state); u32 get_plane_size(struct gsc_frame *fr, unsigned int plane); const struct gsc_fmt *get_format(int index); const struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, u32 index); -int gsc_enum_fmt_mplane(struct v4l2_fmtdesc *f); +int gsc_enum_fmt(struct v4l2_fmtdesc *f); int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f); void gsc_set_frame_size(struct gsc_frame *frame, int width, int height); int gsc_g_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f); diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index 677d7cc80785..35a1d0d6dd66 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -294,15 +294,13 @@ static int gsc_m2m_querycap(struct file *file, void *fh, strscpy(cap->card, GSC_MODULE_NAME " gscaler", sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev_name(&gsc->pdev->dev)); - cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } -static int gsc_m2m_enum_fmt_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) +static int gsc_m2m_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { - return gsc_enum_fmt_mplane(f); + return gsc_enum_fmt(f); } static int gsc_m2m_g_fmt_mplane(struct file *file, void *fh, @@ -558,8 +556,8 @@ static int gsc_m2m_s_selection(struct file *file, void *fh, static const struct v4l2_ioctl_ops gsc_m2m_ioctl_ops = { .vidioc_querycap = gsc_m2m_querycap, - .vidioc_enum_fmt_vid_cap_mplane = gsc_m2m_enum_fmt_mplane, - .vidioc_enum_fmt_vid_out_mplane = gsc_m2m_enum_fmt_mplane, + .vidioc_enum_fmt_vid_cap = gsc_m2m_enum_fmt, + .vidioc_enum_fmt_vid_out = gsc_m2m_enum_fmt, .vidioc_g_fmt_vid_cap_mplane = gsc_m2m_g_fmt_mplane, .vidioc_g_fmt_vid_out_mplane = gsc_m2m_g_fmt_mplane, .vidioc_try_fmt_vid_cap_mplane = gsc_m2m_try_fmt_mplane, @@ -759,6 +757,8 @@ int gsc_register_m2m_device(struct gsc_dev *gsc) gsc->vdev.lock = &gsc->lock; gsc->vdev.vfl_dir = VFL_DIR_M2M; gsc->vdev.v4l2_dev = &gsc->v4l2_dev; + gsc->vdev.device_caps = V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_M2M_MPLANE; snprintf(gsc->vdev.name, sizeof(gsc->vdev.name), "%s.%d:m2m", GSC_MODULE_NAME, gsc->id); diff --git a/drivers/media/platform/exynos4-is/common.c b/drivers/media/platform/exynos4-is/common.c index b4e30e7c8a4b..944b224eb621 100644 --- a/drivers/media/platform/exynos4-is/common.c +++ b/drivers/media/platform/exynos4-is/common.c @@ -34,15 +34,12 @@ struct v4l2_subdev *fimc_find_remote_sensor(struct media_entity *entity) } EXPORT_SYMBOL(fimc_find_remote_sensor); -void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap, - unsigned int caps) +void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap) { strscpy(cap->driver, dev->driver->name, sizeof(cap->driver)); strscpy(cap->card, dev->driver->name, sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev_name(dev)); - cap->device_caps = caps; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; } EXPORT_SYMBOL(__fimc_vidioc_querycap); diff --git a/drivers/media/platform/exynos4-is/common.h b/drivers/media/platform/exynos4-is/common.h index 41de3f716691..0389b66e5144 100644 --- a/drivers/media/platform/exynos4-is/common.h +++ b/drivers/media/platform/exynos4-is/common.h @@ -9,5 +9,4 @@ #include <media/v4l2-subdev.h> struct v4l2_subdev *fimc_find_remote_sensor(struct media_entity *entity); -void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap, - unsigned int caps); +void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap); diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index bce94681cbf0..66510365dd5d 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -725,13 +725,12 @@ static int fimc_cap_querycap(struct file *file, void *priv, { struct fimc_dev *fimc = video_drvdata(file); - __fimc_vidioc_querycap(&fimc->pdev->dev, cap, V4L2_CAP_STREAMING | - V4L2_CAP_VIDEO_CAPTURE_MPLANE); + __fimc_vidioc_querycap(&fimc->pdev->dev, cap); return 0; } -static int fimc_cap_enum_fmt_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) +static int fimc_cap_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { struct fimc_fmt *fmt; @@ -1358,7 +1357,7 @@ static int fimc_cap_s_selection(struct file *file, void *fh, static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = { .vidioc_querycap = fimc_cap_querycap, - .vidioc_enum_fmt_vid_cap_mplane = fimc_cap_enum_fmt_mplane, + .vidioc_enum_fmt_vid_cap = fimc_cap_enum_fmt, .vidioc_try_fmt_vid_cap_mplane = fimc_cap_try_fmt_mplane, .vidioc_s_fmt_vid_cap_mplane = fimc_cap_s_fmt_mplane, .vidioc_g_fmt_vid_cap_mplane = fimc_cap_g_fmt_mplane, @@ -1762,6 +1761,7 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, vfd->release = video_device_release_empty; vfd->queue = q; vfd->lock = &fimc->lock; + vfd->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE; video_set_drvdata(vfd, fimc); vid_cap = &fimc->vid_cap; diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c index 8900559e1813..a75f932a289a 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c @@ -346,12 +346,12 @@ static int isp_video_querycap(struct file *file, void *priv, { struct fimc_isp *isp = video_drvdata(file); - __fimc_vidioc_querycap(&isp->pdev->dev, cap, V4L2_CAP_STREAMING); + __fimc_vidioc_querycap(&isp->pdev->dev, cap); return 0; } -static int isp_video_enum_fmt_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) +static int isp_video_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { const struct fimc_fmt *fmt; @@ -548,7 +548,7 @@ static int isp_video_reqbufs(struct file *file, void *priv, static const struct v4l2_ioctl_ops isp_video_ioctl_ops = { .vidioc_querycap = isp_video_querycap, - .vidioc_enum_fmt_vid_cap_mplane = isp_video_enum_fmt_mplane, + .vidioc_enum_fmt_vid_cap = isp_video_enum_fmt, .vidioc_try_fmt_vid_cap_mplane = isp_video_try_fmt_mplane, .vidioc_s_fmt_vid_cap_mplane = isp_video_s_fmt_mplane, .vidioc_g_fmt_vid_cap_mplane = isp_video_g_fmt_mplane, @@ -611,6 +611,7 @@ int fimc_isp_video_device_register(struct fimc_isp *isp, vdev->minor = -1; vdev->release = video_device_release_empty; vdev->lock = &isp->video_lock; + vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE; iv->pad.flags = MEDIA_PAD_FL_SINK; ret = media_entity_pads_init(&vdev->entity, 1, &iv->pad); diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index 347b90088b91..c1f0aee02e5e 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -655,14 +655,11 @@ static int fimc_lite_querycap(struct file *file, void *priv, strscpy(cap->card, FIMC_LITE_DRV_NAME, sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev_name(&fimc->pdev->dev)); - - cap->device_caps = V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } -static int fimc_lite_enum_fmt_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) +static int fimc_lite_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { const struct fimc_fmt *fmt; @@ -951,7 +948,7 @@ static int fimc_lite_s_selection(struct file *file, void *fh, static const struct v4l2_ioctl_ops fimc_lite_ioctl_ops = { .vidioc_querycap = fimc_lite_querycap, - .vidioc_enum_fmt_vid_cap_mplane = fimc_lite_enum_fmt_mplane, + .vidioc_enum_fmt_vid_cap = fimc_lite_enum_fmt, .vidioc_try_fmt_vid_cap_mplane = fimc_lite_try_fmt_mplane, .vidioc_s_fmt_vid_cap_mplane = fimc_lite_s_fmt_mplane, .vidioc_g_fmt_vid_cap_mplane = fimc_lite_g_fmt_mplane, @@ -1279,6 +1276,7 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd) vfd->minor = -1; vfd->release = video_device_release_empty; vfd->queue = q; + vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING; fimc->reqbufs_count = 0; INIT_LIST_HEAD(&fimc->pending_buf_q); diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c index b950c152fa28..62e876fc3555 100644 --- a/drivers/media/platform/exynos4-is/fimc-m2m.c +++ b/drivers/media/platform/exynos4-is/fimc-m2m.c @@ -232,14 +232,13 @@ static int fimc_m2m_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { struct fimc_dev *fimc = video_drvdata(file); - unsigned int caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; - __fimc_vidioc_querycap(&fimc->pdev->dev, cap, caps); + __fimc_vidioc_querycap(&fimc->pdev->dev, cap); return 0; } -static int fimc_m2m_enum_fmt_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) +static int fimc_m2m_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { struct fimc_fmt *fmt; @@ -529,8 +528,8 @@ static int fimc_m2m_s_selection(struct file *file, void *fh, static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = { .vidioc_querycap = fimc_m2m_querycap, - .vidioc_enum_fmt_vid_cap_mplane = fimc_m2m_enum_fmt_mplane, - .vidioc_enum_fmt_vid_out_mplane = fimc_m2m_enum_fmt_mplane, + .vidioc_enum_fmt_vid_cap = fimc_m2m_enum_fmt, + .vidioc_enum_fmt_vid_out = fimc_m2m_enum_fmt, .vidioc_g_fmt_vid_cap_mplane = fimc_m2m_g_fmt_mplane, .vidioc_g_fmt_vid_out_mplane = fimc_m2m_g_fmt_mplane, .vidioc_try_fmt_vid_cap_mplane = fimc_m2m_try_fmt_mplane, @@ -732,6 +731,7 @@ int fimc_register_m2m_device(struct fimc_dev *fimc, vfd->release = video_device_release_empty; vfd->lock = &fimc->lock; vfd->vfl_dir = VFL_DIR_M2M; + vfd->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; set_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags); snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.m2m", fimc->id); diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 1b83a6ec745f..d53427a8db11 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -445,6 +445,7 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd, pd->fimc_bus_type = FIMC_BUS_TYPE_ISP_WRITEBACK; else pd->fimc_bus_type = pd->sensor_bus_type; + of_node_put(np); if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor))) { of_node_put(rem); @@ -470,7 +471,8 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd, static int fimc_md_register_sensor_entities(struct fimc_md *fmd) { struct device_node *parent = fmd->pdev->dev.of_node; - struct device_node *node, *ports; + struct device_node *ports = NULL; + struct device_node *node; int index = 0; int ret; @@ -519,12 +521,14 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) } index++; } + of_node_put(ports); rpm_put: pm_runtime_put(fmd->pmf); return 0; cleanup: + of_node_put(ports); v4l2_async_notifier_cleanup(&fmd->subdev_notifier); pm_runtime_put(fmd->pmf); return ret; diff --git a/drivers/media/platform/marvell-ccic/Kconfig b/drivers/media/platform/marvell-ccic/Kconfig index 86b84474dd8c..3e3f86264762 100644 --- a/drivers/media/platform/marvell-ccic/Kconfig +++ b/drivers/media/platform/marvell-ccic/Kconfig @@ -2,6 +2,7 @@ config VIDEO_CAFE_CCIC tristate "Marvell 88ALP01 (Cafe) CMOS Camera Controller support" depends on PCI && I2C && VIDEO_V4L2 + depends on COMMON_CLK select VIDEO_OV7670 select VIDEOBUF2_VMALLOC select VIDEOBUF2_DMA_CONTIG @@ -15,6 +16,7 @@ config VIDEO_MMP_CAMERA tristate "Marvell Armada 610 integrated camera controller support" depends on I2C && VIDEO_V4L2 depends on ARCH_MMP || COMPILE_TEST + depends on COMMON_CLK select VIDEO_OV7670 select I2C_GPIO select VIDEOBUF2_VMALLOC diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c index cd108b14b715..37fdcc53a1c4 100644 --- a/drivers/media/platform/marvell-ccic/cafe-driver.c +++ b/drivers/media/platform/marvell-ccic/cafe-driver.c @@ -9,6 +9,7 @@ * * Copyright 2006-11 One Laptop Per Child Association, Inc. * Copyright 2006-11 Jonathan Corbet <corbet@lwn.net> + * Copyright 2018 Lubomir Rintel <lkundrak@v3.sk> * * Written by Jonathan Corbet, corbet@lwn.net. * @@ -25,10 +26,12 @@ #include <linux/slab.h> #include <linux/videodev2.h> #include <media/v4l2-device.h> +#include <media/i2c/ov7670.h> #include <linux/device.h> #include <linux/wait.h> #include <linux/delay.h> #include <linux/io.h> +#include <linux/clkdev.h> #include "mcam-core.h" @@ -50,6 +53,7 @@ struct cafe_camera { int registered; /* Fully initialized? */ struct mcam_camera mcam; struct pci_dev *pdev; + struct i2c_adapter *i2c_adapter; wait_queue_head_t smbus_wait; /* Waiting on i2c events */ }; @@ -349,15 +353,15 @@ static int cafe_smbus_setup(struct cafe_camera *cam) return ret; } - cam->mcam.i2c_adapter = adap; + cam->i2c_adapter = adap; cafe_smbus_enable_irq(cam); return 0; } static void cafe_smbus_shutdown(struct cafe_camera *cam) { - i2c_del_adapter(cam->mcam.i2c_adapter); - kfree(cam->mcam.i2c_adapter); + i2c_del_adapter(cam->i2c_adapter); + kfree(cam->i2c_adapter); } @@ -450,6 +454,29 @@ static irqreturn_t cafe_irq(int irq, void *data) return IRQ_RETVAL(handled); } +/* -------------------------------------------------------------------------- */ + +static struct ov7670_config sensor_cfg = { + /* + * Exclude QCIF mode, because it only captures a tiny portion + * of the sensor FOV + */ + .min_width = 320, + .min_height = 240, + + /* + * Set the clock speed for the XO 1; I don't believe this + * driver has ever run anywhere else. + */ + .clock_speed = 45, + .use_smbus = 1, +}; + +static struct i2c_board_info ov7670_info = { + .type = "ov7670", + .addr = 0x42 >> 1, + .platform_data = &sensor_cfg, +}; /* -------------------------------------------------------------------------- */ /* @@ -480,12 +507,6 @@ static int cafe_pci_probe(struct pci_dev *pdev, mcam->dev = &pdev->dev; snprintf(mcam->bus_info, sizeof(mcam->bus_info), "PCI:%s", pci_name(pdev)); /* - * Set the clock speed for the XO 1; I don't believe this - * driver has ever run anywhere else. - */ - mcam->clock_speed = 45; - mcam->use_smbus = 1; - /* * Vmalloc mode for buffers is traditional with this driver. * We *might* be able to run DMA_contig, especially on a system * with CMA in it. @@ -511,11 +532,10 @@ static int cafe_pci_probe(struct pci_dev *pdev, goto out_iounmap; /* - * Initialize the controller and leave it powered up. It will - * stay that way until the sensor driver shows up. + * Initialize the controller. */ cafe_ctlr_init(mcam); - cafe_ctlr_power_up(mcam); + /* * Set up I2C/SMBUS communications. We have to drop the mutex here * because the sensor could attach in this call chain, leading to @@ -525,12 +545,24 @@ static int cafe_pci_probe(struct pci_dev *pdev, if (ret) goto out_pdown; + mcam->asd.match_type = V4L2_ASYNC_MATCH_I2C; + mcam->asd.match.i2c.adapter_id = i2c_adapter_id(cam->i2c_adapter); + mcam->asd.match.i2c.address = ov7670_info.addr; + ret = mccic_register(mcam); - if (ret == 0) { + if (ret) + goto out_smbus_shutdown; + + clkdev_create(mcam->mclk, "xclk", "%d-%04x", + i2c_adapter_id(cam->i2c_adapter), ov7670_info.addr); + + if (i2c_new_device(cam->i2c_adapter, &ov7670_info)) { cam->registered = 1; return 0; } + mccic_shutdown(mcam); +out_smbus_shutdown: cafe_smbus_shutdown(cam); out_pdown: cafe_ctlr_power_down(mcam); diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index f1b301810260..dc30c48d4671 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -4,6 +4,7 @@ * so it needs platform-specific support outside of the core. * * Copyright 2011 Jonathan Corbet corbet@lwn.net + * Copyright 2018 Lubomir Rintel <lkundrak@v3.sk> */ #include <linux/kernel.h> #include <linux/module.h> @@ -21,12 +22,12 @@ #include <linux/vmalloc.h> #include <linux/io.h> #include <linux/clk.h> +#include <linux/clk-provider.h> #include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-event.h> -#include <media/i2c/ov7670.h> #include <media/videobuf2-vmalloc.h> #include <media/videobuf2-dma-contig.h> #include <media/videobuf2-dma-sg.h> @@ -93,6 +94,9 @@ MODULE_PARM_DESC(buffer_mode, #define sensor_call(cam, o, f, args...) \ v4l2_subdev_call(cam->sensor, o, f, ##args) +#define notifier_to_mcam(notifier) \ + container_of(notifier, struct mcam_camera, notifier) + static struct mcam_format_struct { __u8 *desc; __u32 pixelformat; @@ -200,7 +204,6 @@ struct mcam_vb_buffer { struct list_head queue; struct mcam_dma_desc *dma_desc; /* Descriptor virtual address */ dma_addr_t dma_desc_pa; /* Descriptor physical address */ - int dma_desc_nent; /* Number of mapped descriptors */ }; static inline struct mcam_vb_buffer *vb_to_mvb(struct vb2_v4l2_buffer *vb) @@ -282,6 +285,8 @@ static void mcam_ctlr_stop(struct mcam_camera *cam) static void mcam_enable_mipi(struct mcam_camera *mcam) { /* Using MIPI mode and enable MIPI */ + if (mcam->calc_dphy) + mcam->calc_dphy(mcam); cam_dbg(mcam, "camera: DPHY3=0x%x, DPHY5=0x%x, DPHY6=0x%x\n", mcam->dphy[0], mcam->dphy[1], mcam->dphy[2]); mcam_reg_write(mcam, REG_CSI2_DPHY3, mcam->dphy[0]); @@ -301,9 +306,6 @@ static void mcam_enable_mipi(struct mcam_camera *mcam) */ mcam_reg_write(mcam, REG_CSI2_CTRL0, CSI2_C0_MIPI_EN | CSI2_C0_ACT_LANE(mcam->lane)); - mcam_reg_write(mcam, REG_CLKCTRL, - (mcam->mclk_src << 29) | mcam->mclk_div); - mcam->mipi_enabled = true; } } @@ -608,9 +610,11 @@ static void mcam_dma_contig_done(struct mcam_camera *cam, int frame) static void mcam_sg_next_buffer(struct mcam_camera *cam) { struct mcam_vb_buffer *buf; + struct sg_table *sg_table; buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue); list_del_init(&buf->queue); + sg_table = vb2_dma_sg_plane_desc(&buf->vb_buf.vb2_buf, 0); /* * Very Bad Not Good Things happen if you don't clear * C1_DESC_ENA before making any descriptor changes. @@ -618,7 +622,7 @@ static void mcam_sg_next_buffer(struct mcam_camera *cam) mcam_reg_clear_bit(cam, REG_CTRL1, C1_DESC_ENA); mcam_reg_write(cam, REG_DMA_DESC_Y, buf->dma_desc_pa); mcam_reg_write(cam, REG_DESC_LEN_Y, - buf->dma_desc_nent*sizeof(struct mcam_dma_desc)); + sg_table->nents * sizeof(struct mcam_dma_desc)); mcam_reg_write(cam, REG_DESC_LEN_U, 0); mcam_reg_write(cam, REG_DESC_LEN_V, 0); mcam_reg_set_bit(cam, REG_CTRL1, C1_DESC_ENA); @@ -791,12 +795,6 @@ static void mcam_ctlr_image(struct mcam_camera *cam) * Make sure it knows we want to use hsync/vsync. */ mcam_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC, C0_SIFM_MASK); - /* - * This field controls the generation of EOF(DVP only) - */ - if (cam->bus_type != V4L2_MBUS_CSI2_DPHY) - mcam_reg_set_bit(cam, REG_CTRL0, - C0_EOF_VSYNC | C0_VEDGE_CTRL); } @@ -832,31 +830,6 @@ static void mcam_ctlr_irq_disable(struct mcam_camera *cam) mcam_reg_clear_bit(cam, REG_IRQMASK, FRAMEIRQS); } - - -static void mcam_ctlr_init(struct mcam_camera *cam) -{ - unsigned long flags; - - spin_lock_irqsave(&cam->dev_lock, flags); - /* - * Make sure it's not powered down. - */ - mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN); - /* - * Turn off the enable bit. It sure should be off anyway, - * but it's good to be sure. - */ - mcam_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE); - /* - * Clock the sensor appropriately. Controller clock should - * be 48MHz, sensor "typical" value is half that. - */ - mcam_reg_write_mask(cam, REG_CLKCTRL, 2, CLK_DIV_MASK); - spin_unlock_irqrestore(&cam->dev_lock, flags); -} - - /* * Stop the controller, and don't return until we're really sure that no * further DMA is going on. @@ -900,14 +873,15 @@ static int mcam_ctlr_power_up(struct mcam_camera *cam) int ret; spin_lock_irqsave(&cam->dev_lock, flags); - ret = cam->plat_power_up(cam); - if (ret) { - spin_unlock_irqrestore(&cam->dev_lock, flags); - return ret; + if (cam->plat_power_up) { + ret = cam->plat_power_up(cam); + if (ret) { + spin_unlock_irqrestore(&cam->dev_lock, flags); + return ret; + } } mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN); spin_unlock_irqrestore(&cam->dev_lock, flags); - msleep(5); /* Just to be sure */ return 0; } @@ -922,10 +896,101 @@ static void mcam_ctlr_power_down(struct mcam_camera *cam) * power down routine. */ mcam_reg_set_bit(cam, REG_CTRL1, C1_PWRDWN); - cam->plat_power_down(cam); + if (cam->plat_power_down) + cam->plat_power_down(cam); spin_unlock_irqrestore(&cam->dev_lock, flags); } +/* ---------------------------------------------------------------------- */ +/* + * Controller clocks. + */ +static void mcam_clk_enable(struct mcam_camera *mcam) +{ + unsigned int i; + + for (i = 0; i < NR_MCAM_CLK; i++) { + if (!IS_ERR(mcam->clk[i])) + clk_prepare_enable(mcam->clk[i]); + } +} + +static void mcam_clk_disable(struct mcam_camera *mcam) +{ + int i; + + for (i = NR_MCAM_CLK - 1; i >= 0; i--) { + if (!IS_ERR(mcam->clk[i])) + clk_disable_unprepare(mcam->clk[i]); + } +} + +/* ---------------------------------------------------------------------- */ +/* + * Master sensor clock. + */ +static int mclk_prepare(struct clk_hw *hw) +{ + struct mcam_camera *cam = container_of(hw, struct mcam_camera, mclk_hw); + + clk_prepare(cam->clk[0]); + return 0; +} + +static void mclk_unprepare(struct clk_hw *hw) +{ + struct mcam_camera *cam = container_of(hw, struct mcam_camera, mclk_hw); + + clk_unprepare(cam->clk[0]); +} + +static int mclk_enable(struct clk_hw *hw) +{ + struct mcam_camera *cam = container_of(hw, struct mcam_camera, mclk_hw); + int mclk_src; + int mclk_div; + + /* + * Clock the sensor appropriately. Controller clock should + * be 48MHz, sensor "typical" value is half that. + */ + if (cam->bus_type == V4L2_MBUS_CSI2_DPHY) { + mclk_src = cam->mclk_src; + mclk_div = cam->mclk_div; + } else { + mclk_src = 3; + mclk_div = 2; + } + + clk_enable(cam->clk[0]); + mcam_reg_write(cam, REG_CLKCTRL, (mclk_src << 29) | mclk_div); + mcam_ctlr_power_up(cam); + + return 0; +} + +static void mclk_disable(struct clk_hw *hw) +{ + struct mcam_camera *cam = container_of(hw, struct mcam_camera, mclk_hw); + + mcam_ctlr_power_down(cam); + clk_disable(cam->clk[0]); +} + +static unsigned long mclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return 48000000; +} + +static const struct clk_ops mclk_ops = { + .prepare = mclk_prepare, + .unprepare = mclk_unprepare, + .enable = mclk_enable, + .disable = mclk_disable, + .recalc_rate = mclk_recalc_rate, +}; + /* -------------------------------------------------------------------- */ /* * Communications with the sensor. @@ -950,7 +1015,6 @@ static int mcam_cam_init(struct mcam_camera *cam) ret = __mcam_cam_reset(cam); /* Get/set parameters? */ cam->state = S_IDLE; - mcam_ctlr_power_down(cam); return ret; } @@ -1016,13 +1080,6 @@ static int mcam_read_setup(struct mcam_camera *cam) spin_lock_irqsave(&cam->dev_lock, flags); clear_bit(CF_DMA_ACTIVE, &cam->flags); mcam_reset_buffers(cam); - /* - * Update CSI2_DPHY value - */ - if (cam->calc_dphy) - cam->calc_dphy(cam); - cam_dbg(cam, "camera: DPHY sets: dphy3=0x%x, dphy5=0x%x, dphy6=0x%x\n", - cam->dphy[0], cam->dphy[1], cam->dphy[2]); if (cam->bus_type == V4L2_MBUS_CSI2_DPHY) mcam_enable_mipi(cam); else @@ -1160,12 +1217,6 @@ static void mcam_vb_stop_streaming(struct vb2_queue *vq) return; mcam_ctlr_stop_dma(cam); /* - * Reset the CCIC PHY after stopping streaming, - * otherwise, the CCIC may be unstable. - */ - if (cam->ctlr_reset) - cam->ctlr_reset(cam); - /* * VB2 reclaims the buffers, so we need to forget * about them. */ @@ -1592,9 +1643,10 @@ static int mcam_v4l_open(struct file *filp) if (ret) goto out; if (v4l2_fh_is_singular_file(filp)) { - ret = mcam_ctlr_power_up(cam); + ret = sensor_call(cam, core, s_power, 1); if (ret) goto out; + mcam_clk_enable(cam); __mcam_cam_reset(cam); mcam_set_config_needed(cam, 1); } @@ -1616,7 +1668,8 @@ static int mcam_v4l_release(struct file *filp) _vb2_fop_release(filp, NULL); if (last_open) { mcam_disable_mipi(cam); - mcam_ctlr_power_down(cam); + sensor_call(cam, core, s_power, 0); + mcam_clk_disable(cam); if (cam->buffer_mode == B_vmalloc && alloc_bufs_at_read) mcam_free_dma_bufs(cam); } @@ -1726,23 +1779,95 @@ EXPORT_SYMBOL_GPL(mccic_irq); /* * Registration and such. */ -static struct ov7670_config sensor_cfg = { + +static int mccic_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) +{ + struct mcam_camera *cam = notifier_to_mcam(notifier); + int ret; + + mutex_lock(&cam->s_mutex); + if (cam->sensor) { + cam_err(cam, "sensor already bound\n"); + ret = -EBUSY; + goto out; + } + + v4l2_set_subdev_hostdata(subdev, cam); + cam->sensor = subdev; + + ret = mcam_cam_init(cam); + if (ret) { + cam->sensor = NULL; + goto out; + } + + ret = mcam_setup_vb2(cam); + if (ret) { + cam->sensor = NULL; + goto out; + } + + cam->vdev = mcam_v4l_template; + cam->vdev.v4l2_dev = &cam->v4l2_dev; + cam->vdev.lock = &cam->s_mutex; + cam->vdev.queue = &cam->vb_queue; + video_set_drvdata(&cam->vdev, cam); + ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); + if (ret) { + cam->sensor = NULL; + goto out; + } + + cam_dbg(cam, "sensor %s bound\n", subdev->name); +out: + mutex_unlock(&cam->s_mutex); + return ret; +} + +static void mccic_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) +{ + struct mcam_camera *cam = notifier_to_mcam(notifier); + + mutex_lock(&cam->s_mutex); + if (cam->sensor != subdev) { + cam_err(cam, "sensor %s not bound\n", subdev->name); + goto out; + } + + video_unregister_device(&cam->vdev); + cam->sensor = NULL; + cam_dbg(cam, "sensor %s unbound\n", subdev->name); + +out: + mutex_unlock(&cam->s_mutex); +} + +static int mccic_notify_complete(struct v4l2_async_notifier *notifier) +{ + struct mcam_camera *cam = notifier_to_mcam(notifier); + int ret; + /* - * Exclude QCIF mode, because it only captures a tiny portion - * of the sensor FOV + * Get the v4l2 setup done. */ - .min_width = 320, - .min_height = 240, -}; + ret = v4l2_ctrl_handler_init(&cam->ctrl_handler, 10); + if (!ret) + cam->v4l2_dev.ctrl_handler = &cam->ctrl_handler; + return ret; +} + +static const struct v4l2_async_notifier_operations mccic_notify_ops = { + .bound = mccic_notify_bound, + .unbind = mccic_notify_unbind, + .complete = mccic_notify_complete, +}; int mccic_register(struct mcam_camera *cam) { - struct i2c_board_info ov7670_info = { - .type = "ov7670", - .addr = 0x42 >> 1, - .platform_data = &sensor_cfg, - }; + struct clk_init_data mclk_init = { }; int ret; /* @@ -1755,64 +1880,62 @@ int mccic_register(struct mcam_camera *cam) printk(KERN_ERR "marvell-cam: Cafe can't do S/G I/O, attempting vmalloc mode instead\n"); cam->buffer_mode = B_vmalloc; } + if (!mcam_buffer_mode_supported(cam->buffer_mode)) { printk(KERN_ERR "marvell-cam: buffer mode %d unsupported\n", cam->buffer_mode); - return -EINVAL; + ret = -EINVAL; + goto out; } + /* * Register with V4L */ ret = v4l2_device_register(cam->dev, &cam->v4l2_dev); if (ret) - return ret; + goto out; mutex_init(&cam->s_mutex); cam->state = S_NOTREADY; mcam_set_config_needed(cam, 1); cam->pix_format = mcam_def_pix_format; cam->mbus_code = mcam_def_mbus_code; - mcam_ctlr_init(cam); /* - * Get the v4l2 setup done. + * Register sensor notifier. */ - ret = v4l2_ctrl_handler_init(&cam->ctrl_handler, 10); - if (ret) - goto out_unregister; - cam->v4l2_dev.ctrl_handler = &cam->ctrl_handler; + v4l2_async_notifier_init(&cam->notifier); + ret = v4l2_async_notifier_add_subdev(&cam->notifier, &cam->asd); + if (ret) { + cam_warn(cam, "failed to add subdev to a notifier"); + goto out; + } + + cam->notifier.ops = &mccic_notify_ops; + ret = v4l2_async_notifier_register(&cam->v4l2_dev, &cam->notifier); + if (ret < 0) { + cam_warn(cam, "failed to register a sensor notifier"); + goto out; + } /* - * Try to find the sensor. + * Register sensor master clock. */ - sensor_cfg.clock_speed = cam->clock_speed; - sensor_cfg.use_smbus = cam->use_smbus; - cam->sensor_addr = ov7670_info.addr; - cam->sensor = v4l2_i2c_new_subdev_board(&cam->v4l2_dev, - cam->i2c_adapter, &ov7670_info, NULL); - if (cam->sensor == NULL) { - ret = -ENODEV; - goto out_unregister; - } + mclk_init.parent_names = NULL; + mclk_init.num_parents = 0; + mclk_init.ops = &mclk_ops; + mclk_init.name = "mclk"; - ret = mcam_cam_init(cam); - if (ret) - goto out_unregister; + of_property_read_string(cam->dev->of_node, "clock-output-names", + &mclk_init.name); - ret = mcam_setup_vb2(cam); - if (ret) - goto out_unregister; + cam->mclk_hw.init = &mclk_init; - mutex_lock(&cam->s_mutex); - cam->vdev = mcam_v4l_template; - cam->vdev.v4l2_dev = &cam->v4l2_dev; - cam->vdev.lock = &cam->s_mutex; - cam->vdev.queue = &cam->vb_queue; - video_set_drvdata(&cam->vdev, cam); - ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); - if (ret) { - mutex_unlock(&cam->s_mutex); - goto out_unregister; + cam->mclk = devm_clk_register(cam->dev, &cam->mclk_hw); + if (IS_ERR(cam->mclk)) { + ret = PTR_ERR(cam->mclk); + dev_err(cam->dev, "can't register clock\n"); + goto out; } /* @@ -1823,11 +1946,10 @@ int mccic_register(struct mcam_camera *cam) cam_warn(cam, "Unable to alloc DMA buffers at load will try again later."); } - mutex_unlock(&cam->s_mutex); return 0; -out_unregister: - v4l2_ctrl_handler_free(&cam->ctrl_handler); +out: + v4l2_async_notifier_unregister(&cam->notifier); v4l2_device_unregister(&cam->v4l2_dev); return ret; } @@ -1843,12 +1965,12 @@ void mccic_shutdown(struct mcam_camera *cam) */ if (!list_empty(&cam->vdev.fh_list)) { cam_warn(cam, "Removing a device with users!\n"); - mcam_ctlr_power_down(cam); + sensor_call(cam, core, s_power, 0); } if (cam->buffer_mode == B_vmalloc) mcam_free_dma_bufs(cam); - video_unregister_device(&cam->vdev); v4l2_ctrl_handler_free(&cam->ctrl_handler); + v4l2_async_notifier_unregister(&cam->notifier); v4l2_device_unregister(&cam->v4l2_dev); } EXPORT_SYMBOL_GPL(mccic_shutdown); @@ -1865,7 +1987,8 @@ void mccic_suspend(struct mcam_camera *cam) enum mcam_state cstate = cam->state; mcam_ctlr_stop_dma(cam); - mcam_ctlr_power_down(cam); + sensor_call(cam, core, s_power, 0); + mcam_clk_disable(cam); cam->state = cstate; } mutex_unlock(&cam->s_mutex); @@ -1878,14 +2001,15 @@ int mccic_resume(struct mcam_camera *cam) mutex_lock(&cam->s_mutex); if (!list_empty(&cam->vdev.fh_list)) { - ret = mcam_ctlr_power_up(cam); + mcam_clk_enable(cam); + ret = sensor_call(cam, core, s_power, 1); if (ret) { mutex_unlock(&cam->s_mutex); return ret; } __mcam_cam_reset(cam); } else { - mcam_ctlr_power_down(cam); + sensor_call(cam, core, s_power, 0); } mutex_unlock(&cam->s_mutex); diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index ad8955f9f0a1..2e3a7567a76a 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -8,6 +8,7 @@ #define _MCAM_CORE_H #include <linux/list.h> +#include <linux/clk-provider.h> #include <media/v4l2-common.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-dev.h> @@ -102,21 +103,16 @@ struct mcam_camera { * These fields should be set by the platform code prior to * calling mcam_register(). */ - struct i2c_adapter *i2c_adapter; unsigned char __iomem *regs; unsigned regs_size; /* size in bytes of the register space */ spinlock_t dev_lock; struct device *dev; /* For messages, dma alloc */ enum mcam_chip_id chip_id; - short int clock_speed; /* Sensor clock speed, default 30 */ - short int use_smbus; /* SMBUS or straight I2c? */ enum mcam_buffer_mode buffer_mode; - int mclk_min; /* The minimal value of mclk */ int mclk_src; /* which clock source the mclk derives from */ int mclk_div; /* Clock Divider Value for MCLK */ - int ccic_id; enum v4l2_mbus_type bus_type; /* MIPI support */ /* The dphy config value, allocated in board file @@ -130,6 +126,8 @@ struct mcam_camera { /* clock tree support */ struct clk *clk[NR_MCAM_CLK]; + struct clk_hw mclk_hw; + struct clk *mclk; /* * Callbacks from the core to the platform code. @@ -137,7 +135,6 @@ struct mcam_camera { int (*plat_power_up) (struct mcam_camera *cam); void (*plat_power_down) (struct mcam_camera *cam); void (*calc_dphy) (struct mcam_camera *cam); - void (*ctlr_reset) (struct mcam_camera *cam); /* * Everything below here is private to the mcam core and @@ -153,8 +150,9 @@ struct mcam_camera { * Subsystem structures. */ struct video_device vdev; + struct v4l2_async_notifier notifier; + struct v4l2_async_subdev asd; struct v4l2_subdev *sensor; - unsigned short sensor_addr; /* Videobuf2 stuff */ struct vb2_queue vb_queue; diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c index bf4d4a47f1db..10559492e09e 100644 --- a/drivers/media/platform/marvell-ccic/mmp-driver.c +++ b/drivers/media/platform/marvell-ccic/mmp-driver.c @@ -4,13 +4,12 @@ * to work with the Armada 610 as used in the OLPC 1.75 system. * * Copyright 2011 Jonathan Corbet <corbet@lwn.net> + * Copyright 2018 Lubomir Rintel <lkundrak@v3.sk> */ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/i2c.h> -#include <linux/platform_data/i2c-gpio.h> #include <linux/interrupt.h> #include <linux/spinlock.h> #include <linux/slab.h> @@ -18,10 +17,10 @@ #include <media/v4l2-device.h> #include <linux/platform_data/media/mmp-camera.h> #include <linux/device.h> +#include <linux/of.h> +#include <linux/of_platform.h> #include <linux/platform_device.h> -#include <linux/gpio.h> #include <linux/io.h> -#include <linux/delay.h> #include <linux/list.h> #include <linux/pm.h> #include <linux/clk.h> @@ -32,10 +31,9 @@ MODULE_ALIAS("platform:mmp-camera"); MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); MODULE_LICENSE("GPL"); -static char *mcam_clks[] = {"CCICAXICLK", "CCICFUNCLK", "CCICPHYCLK"}; +static char *mcam_clks[] = {"axi", "func", "phy"}; struct mmp_camera { - void __iomem *power_regs; struct platform_device *pdev; struct mcam_camera mcam; struct list_head devlist; @@ -91,118 +89,6 @@ static struct mmp_camera *mmpcam_find_device(struct platform_device *pdev) return NULL; } - - - -/* - * Power-related registers; this almost certainly belongs - * somewhere else. - * - * ARMADA 610 register manual, sec 7.2.1, p1842. - */ -#define CPU_SUBSYS_PMU_BASE 0xd4282800 -#define REG_CCIC_DCGCR 0x28 /* CCIC dyn clock gate ctrl reg */ -#define REG_CCIC_CRCR 0x50 /* CCIC clk reset ctrl reg */ -#define REG_CCIC2_CRCR 0xf4 /* CCIC2 clk reset ctrl reg */ - -static void mcam_clk_enable(struct mcam_camera *mcam) -{ - unsigned int i; - - for (i = 0; i < NR_MCAM_CLK; i++) { - if (!IS_ERR(mcam->clk[i])) - clk_prepare_enable(mcam->clk[i]); - } -} - -static void mcam_clk_disable(struct mcam_camera *mcam) -{ - int i; - - for (i = NR_MCAM_CLK - 1; i >= 0; i--) { - if (!IS_ERR(mcam->clk[i])) - clk_disable_unprepare(mcam->clk[i]); - } -} - -/* - * Power control. - */ -static void mmpcam_power_up_ctlr(struct mmp_camera *cam) -{ - iowrite32(0x3f, cam->power_regs + REG_CCIC_DCGCR); - iowrite32(0x3805b, cam->power_regs + REG_CCIC_CRCR); - mdelay(1); -} - -static int mmpcam_power_up(struct mcam_camera *mcam) -{ - struct mmp_camera *cam = mcam_to_cam(mcam); - struct mmp_camera_platform_data *pdata; - -/* - * Turn on power and clocks to the controller. - */ - mmpcam_power_up_ctlr(cam); -/* - * Provide power to the sensor. - */ - mcam_reg_write(mcam, REG_CLKCTRL, 0x60000002); - pdata = cam->pdev->dev.platform_data; - gpio_set_value(pdata->sensor_power_gpio, 1); - mdelay(5); - mcam_reg_clear_bit(mcam, REG_CTRL1, 0x10000000); - gpio_set_value(pdata->sensor_reset_gpio, 0); /* reset is active low */ - mdelay(5); - gpio_set_value(pdata->sensor_reset_gpio, 1); /* reset is active low */ - mdelay(5); - - mcam_clk_enable(mcam); - - return 0; -} - -static void mmpcam_power_down(struct mcam_camera *mcam) -{ - struct mmp_camera *cam = mcam_to_cam(mcam); - struct mmp_camera_platform_data *pdata; -/* - * Turn off clocks and set reset lines - */ - iowrite32(0, cam->power_regs + REG_CCIC_DCGCR); - iowrite32(0, cam->power_regs + REG_CCIC_CRCR); -/* - * Shut down the sensor. - */ - pdata = cam->pdev->dev.platform_data; - gpio_set_value(pdata->sensor_power_gpio, 0); - gpio_set_value(pdata->sensor_reset_gpio, 0); - - mcam_clk_disable(mcam); -} - -static void mcam_ctlr_reset(struct mcam_camera *mcam) -{ - unsigned long val; - struct mmp_camera *cam = mcam_to_cam(mcam); - - if (mcam->ccic_id) { - /* - * Using CCIC2 - */ - val = ioread32(cam->power_regs + REG_CCIC2_CRCR); - iowrite32(val & ~0x2, cam->power_regs + REG_CCIC2_CRCR); - iowrite32(val | 0x2, cam->power_regs + REG_CCIC2_CRCR); - } else { - /* - * Using CCIC1 - */ - val = ioread32(cam->power_regs + REG_CCIC_CRCR); - iowrite32(val & ~0x2, cam->power_regs + REG_CCIC_CRCR); - iowrite32(val | 0x2, cam->power_regs + REG_CCIC_CRCR); - } -} - /* * calc the dphy register values * There are three dphy registers being used. @@ -334,13 +220,10 @@ static int mmpcam_probe(struct platform_device *pdev) struct mmp_camera *cam; struct mcam_camera *mcam; struct resource *res; + struct fwnode_handle *ep; struct mmp_camera_platform_data *pdata; int ret; - pdata = pdev->dev.platform_data; - if (!pdata) - return -ENODEV; - cam = devm_kzalloc(&pdev->dev, sizeof(*cam), GFP_KERNEL); if (cam == NULL) return -ENOMEM; @@ -348,25 +231,31 @@ static int mmpcam_probe(struct platform_device *pdev) INIT_LIST_HEAD(&cam->devlist); mcam = &cam->mcam; - mcam->plat_power_up = mmpcam_power_up; - mcam->plat_power_down = mmpcam_power_down; - mcam->ctlr_reset = mcam_ctlr_reset; mcam->calc_dphy = mmpcam_calc_dphy; mcam->dev = &pdev->dev; - mcam->use_smbus = 0; - mcam->ccic_id = pdev->id; - mcam->mclk_min = pdata->mclk_min; - mcam->mclk_src = pdata->mclk_src; - mcam->mclk_div = pdata->mclk_div; - mcam->bus_type = pdata->bus_type; - mcam->dphy = pdata->dphy; + pdata = pdev->dev.platform_data; + if (pdata) { + mcam->mclk_src = pdata->mclk_src; + mcam->mclk_div = pdata->mclk_div; + mcam->bus_type = pdata->bus_type; + mcam->dphy = pdata->dphy; + mcam->lane = pdata->lane; + } else { + /* + * These are values that used to be hardcoded in mcam-core and + * work well on a OLPC XO 1.75 with a parallel bus sensor. + * If it turns out other setups make sense, the values should + * be obtained from the device tree. + */ + mcam->mclk_src = 3; + mcam->mclk_div = 2; + } if (mcam->bus_type == V4L2_MBUS_CSI2_DPHY) { cam->mipi_clk = devm_clk_get(mcam->dev, "mipi"); if ((IS_ERR(cam->mipi_clk) && mcam->dphy[2] == 0)) return PTR_ERR(cam->mipi_clk); } mcam->mipi_enabled = false; - mcam->lane = pdata->lane; mcam->chip_id = MCAM_ARMADA610; mcam->buffer_mode = B_DMA_sg; strscpy(mcam->bus_info, "platform:mmp-camera", sizeof(mcam->bus_info)); @@ -379,54 +268,39 @@ static int mmpcam_probe(struct platform_device *pdev) if (IS_ERR(mcam->regs)) return PTR_ERR(mcam->regs); mcam->regs_size = resource_size(res); + + mcam_init_clk(mcam); + /* - * Power/clock memory is elsewhere; get it too. Perhaps this - * should really be managed outside of this driver? - */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - cam->power_regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(cam->power_regs)) - return PTR_ERR(cam->power_regs); - /* - * Find the i2c adapter. This assumes, of course, that the - * i2c bus is already up and functioning. + * Create a match of the sensor against its OF node. */ - mcam->i2c_adapter = platform_get_drvdata(pdata->i2c_device); - if (mcam->i2c_adapter == NULL) { - dev_err(&pdev->dev, "No i2c adapter\n"); + ep = fwnode_graph_get_next_endpoint(of_fwnode_handle(pdev->dev.of_node), + NULL); + if (!ep) return -ENODEV; - } - /* - * Sensor GPIO pins. - */ - ret = devm_gpio_request(&pdev->dev, pdata->sensor_power_gpio, - "cam-power"); - if (ret) { - dev_err(&pdev->dev, "Can't get sensor power gpio %d", - pdata->sensor_power_gpio); - return ret; - } - gpio_direction_output(pdata->sensor_power_gpio, 0); - ret = devm_gpio_request(&pdev->dev, pdata->sensor_reset_gpio, - "cam-reset"); - if (ret) { - dev_err(&pdev->dev, "Can't get sensor reset gpio %d", - pdata->sensor_reset_gpio); - return ret; - } - gpio_direction_output(pdata->sensor_reset_gpio, 0); - mcam_init_clk(mcam); + mcam->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + mcam->asd.match.fwnode = fwnode_graph_get_remote_port_parent(ep); + + fwnode_handle_put(ep); /* - * Power the device up and hand it off to the core. + * Register the device with the core. */ - ret = mmpcam_power_up(mcam); - if (ret) - return ret; ret = mccic_register(mcam); if (ret) - goto out_power_down; + return ret; + + /* + * Add OF clock provider. + */ + ret = of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get, + mcam->mclk); + if (ret) { + dev_err(&pdev->dev, "can't add DT clock provider\n"); + goto out; + } + /* * Finally, set up our IRQ now that the core is ready to * deal with it. @@ -434,7 +308,7 @@ static int mmpcam_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { ret = -ENODEV; - goto out_unregister; + goto out; } cam->irq = res->start; ret = devm_request_irq(&pdev->dev, cam->irq, mmpcam_irq, IRQF_SHARED, @@ -444,10 +318,10 @@ static int mmpcam_probe(struct platform_device *pdev) return 0; } -out_unregister: +out: + fwnode_handle_put(mcam->asd.match.fwnode); mccic_shutdown(mcam); -out_power_down: - mmpcam_power_down(mcam); + return ret; } @@ -458,7 +332,6 @@ static int mmpcam_remove(struct mmp_camera *cam) mmpcam_remove_device(cam); mccic_shutdown(mcam); - mmpcam_power_down(mcam); return 0; } @@ -490,17 +363,15 @@ static int mmpcam_resume(struct platform_device *pdev) { struct mmp_camera *cam = mmpcam_find_device(pdev); - /* - * Power up unconditionally just in case the core tries to - * touch a register even if nothing was active before; trust - * me, it's better this way. - */ - mmpcam_power_up_ctlr(cam); return mccic_resume(&cam->mcam); } #endif +static const struct of_device_id mmpcam_of_match[] = { + { .compatible = "marvell,mmp2-ccic", }, + {}, +}; static struct platform_driver mmpcam_driver = { .probe = mmpcam_probe, @@ -511,6 +382,7 @@ static struct platform_driver mmpcam_driver = { #endif .driver = { .name = "mmp-camera", + .of_match_table = of_match_ptr(mmpcam_of_match), } }; diff --git a/drivers/media/platform/meson/ao-cec-g12a.c b/drivers/media/platform/meson/ao-cec-g12a.c index 3620a1e310f5..fb52e5dd044a 100644 --- a/drivers/media/platform/meson/ao-cec-g12a.c +++ b/drivers/media/platform/meson/ao-cec-g12a.c @@ -365,28 +365,22 @@ static int meson_ao_cec_g12a_read(void *context, unsigned int addr, { struct meson_ao_cec_g12a_device *ao_cec = context; u32 reg = FIELD_PREP(CECB_RW_ADDR, addr); - unsigned long flags; int ret = 0; - spin_lock_irqsave(&ao_cec->cec_reg_lock, flags); - ret = regmap_write(ao_cec->regmap, CECB_RW_REG, reg); if (ret) - goto read_out; + return ret; ret = regmap_read_poll_timeout(ao_cec->regmap, CECB_RW_REG, reg, !(reg & CECB_RW_BUS_BUSY), 5, 1000); if (ret) - goto read_out; + return ret; ret = regmap_read(ao_cec->regmap, CECB_RW_REG, ®); *data = FIELD_GET(CECB_RW_RD_DATA, reg); -read_out: - spin_unlock_irqrestore(&ao_cec->cec_reg_lock, flags); - return ret; } @@ -394,19 +388,11 @@ static int meson_ao_cec_g12a_write(void *context, unsigned int addr, unsigned int data) { struct meson_ao_cec_g12a_device *ao_cec = context; - unsigned long flags; u32 reg = FIELD_PREP(CECB_RW_ADDR, addr) | FIELD_PREP(CECB_RW_WR_DATA, data) | CECB_RW_WRITE_EN; - int ret = 0; - spin_lock_irqsave(&ao_cec->cec_reg_lock, flags); - - ret = regmap_write(ao_cec->regmap, CECB_RW_REG, reg); - - spin_unlock_irqrestore(&ao_cec->cec_reg_lock, flags); - - return ret; + return regmap_write(ao_cec->regmap, CECB_RW_REG, reg); } static const struct regmap_config meson_ao_cec_g12a_cec_regmap_conf = { @@ -415,7 +401,6 @@ static const struct regmap_config meson_ao_cec_g12a_cec_regmap_conf = { .reg_read = meson_ao_cec_g12a_read, .reg_write = meson_ao_cec_g12a_write, .max_register = 0xffff, - .fast_io = true, }; static inline void diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c index 656444e7ca2b..ee802fc3bcdf 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c @@ -518,7 +518,7 @@ static int mtk_jpeg_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) return -EINVAL; } - vb = vq->bufs[buf->index]; + vb = vb2_get_buffer(vq, buf->index); jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(vb); jpeg_src_buf->flags = (buf->m.planes[0].bytesused == 0) ? MTK_JPEG_BUF_FLAGS_LAST_FRAME : MTK_JPEG_BUF_FLAGS_INIT; @@ -528,8 +528,8 @@ end: static const struct v4l2_ioctl_ops mtk_jpeg_ioctl_ops = { .vidioc_querycap = mtk_jpeg_querycap, - .vidioc_enum_fmt_vid_cap_mplane = mtk_jpeg_enum_fmt_vid_cap, - .vidioc_enum_fmt_vid_out_mplane = mtk_jpeg_enum_fmt_vid_out, + .vidioc_enum_fmt_vid_cap = mtk_jpeg_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_out = mtk_jpeg_enum_fmt_vid_out, .vidioc_try_fmt_vid_cap_mplane = mtk_jpeg_try_fmt_vid_cap_mplane, .vidioc_try_fmt_vid_out_mplane = mtk_jpeg_try_fmt_vid_out_mplane, .vidioc_g_fmt_vid_cap_mplane = mtk_jpeg_g_fmt_vid_mplane, diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c index b28e3dd4885c..7c9e2d69e21a 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c @@ -612,7 +612,7 @@ static int mtk_mdp_m2m_querycap(struct file *file, void *fh, return 0; } -static int mtk_mdp_enum_fmt_mplane(struct v4l2_fmtdesc *f, u32 type) +static int mtk_mdp_enum_fmt(struct v4l2_fmtdesc *f, u32 type) { const struct mtk_mdp_fmt *fmt; @@ -625,16 +625,16 @@ static int mtk_mdp_enum_fmt_mplane(struct v4l2_fmtdesc *f, u32 type) return 0; } -static int mtk_mdp_m2m_enum_fmt_mplane_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) +static int mtk_mdp_m2m_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { - return mtk_mdp_enum_fmt_mplane(f, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + return mtk_mdp_enum_fmt(f, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); } -static int mtk_mdp_m2m_enum_fmt_mplane_vid_out(struct file *file, void *priv, - struct v4l2_fmtdesc *f) +static int mtk_mdp_m2m_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { - return mtk_mdp_enum_fmt_mplane(f, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + return mtk_mdp_enum_fmt(f, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); } static int mtk_mdp_m2m_g_fmt_mplane(struct file *file, void *fh, @@ -927,8 +927,8 @@ static int mtk_mdp_m2m_s_selection(struct file *file, void *fh, static const struct v4l2_ioctl_ops mtk_mdp_m2m_ioctl_ops = { .vidioc_querycap = mtk_mdp_m2m_querycap, - .vidioc_enum_fmt_vid_cap_mplane = mtk_mdp_m2m_enum_fmt_mplane_vid_cap, - .vidioc_enum_fmt_vid_out_mplane = mtk_mdp_m2m_enum_fmt_mplane_vid_out, + .vidioc_enum_fmt_vid_cap = mtk_mdp_m2m_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_out = mtk_mdp_m2m_enum_fmt_vid_out, .vidioc_g_fmt_vid_cap_mplane = mtk_mdp_m2m_g_fmt_mplane, .vidioc_g_fmt_vid_out_mplane = mtk_mdp_m2m_g_fmt_mplane, .vidioc_try_fmt_vid_cap_mplane = mtk_mdp_m2m_try_fmt_mplane, diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c index 7ae588e62ed8..90d1a67db7e5 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> @@ -24,7 +24,7 @@ #define DFT_CFG_WIDTH MTK_VDEC_MIN_W #define DFT_CFG_HEIGHT MTK_VDEC_MIN_H -static struct mtk_video_fmt mtk_video_formats[] = { +static const struct mtk_video_fmt mtk_video_formats[] = { { .fourcc = V4L2_PIX_FMT_H264, .type = MTK_FMT_DEC, @@ -68,9 +68,9 @@ static const struct mtk_codec_framesizes mtk_vdec_framesizes[] = { #define NUM_SUPPORTED_FRAMESIZE ARRAY_SIZE(mtk_vdec_framesizes) #define NUM_FORMATS ARRAY_SIZE(mtk_video_formats) -static struct mtk_video_fmt *mtk_vdec_find_format(struct v4l2_format *f) +static const struct mtk_video_fmt *mtk_vdec_find_format(struct v4l2_format *f) { - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; unsigned int k; for (k = 0; k < NUM_FORMATS; k++) { @@ -122,8 +122,9 @@ static struct vb2_buffer *get_display_buffer(struct mtk_vcodec_ctx *ctx) if (dstbuf->used) { vb2_set_plane_payload(&dstbuf->vb.vb2_buf, 0, ctx->picinfo.fb_sz[0]); - vb2_set_plane_payload(&dstbuf->vb.vb2_buf, 1, - ctx->picinfo.fb_sz[1]); + if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 2) + vb2_set_plane_payload(&dstbuf->vb.vb2_buf, 1, + ctx->picinfo.fb_sz[1]); mtk_v4l2_debug(2, "[%d]status=%x queue id=%d to done_list %d", @@ -271,7 +272,7 @@ static void mtk_vdec_flush_decoder(struct mtk_vcodec_ctx *ctx) static void mtk_vdec_update_fmt(struct mtk_vcodec_ctx *ctx, unsigned int pixelformat) { - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; struct mtk_q_data *dst_q_data; unsigned int k; @@ -394,7 +395,8 @@ static void mtk_vdec_worker(struct work_struct *work) vdec_if_decode(ctx, NULL, NULL, &res_chg); clean_display_buffer(ctx); vb2_set_plane_payload(&dst_buf_info->vb.vb2_buf, 0, 0); - vb2_set_plane_payload(&dst_buf_info->vb.vb2_buf, 1, 0); + if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 2) + vb2_set_plane_payload(&dst_buf_info->vb.vb2_buf, 1, 0); dst_buf->flags |= V4L2_BUF_FLAG_LAST; v4l2_m2m_buf_done(&dst_buf_info->vb, VB2_BUF_STATE_DONE); clean_free_buffer(ctx); @@ -644,7 +646,8 @@ static int vidioc_vdec_subscribe_evt(struct v4l2_fh *fh, } } -static int vidioc_try_fmt(struct v4l2_format *f, struct mtk_video_fmt *fmt) +static int vidioc_try_fmt(struct v4l2_format *f, + const struct mtk_video_fmt *fmt) { struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; int i; @@ -717,7 +720,7 @@ static int vidioc_try_fmt(struct v4l2_format *f, struct mtk_video_fmt *fmt) static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_format *f) { - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; fmt = mtk_vdec_find_format(f); if (!fmt) { @@ -732,7 +735,7 @@ static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; fmt = mtk_vdec_find_format(f); if (!fmt) { @@ -826,7 +829,7 @@ static int vidioc_vdec_s_fmt(struct file *file, void *priv, struct v4l2_pix_format_mplane *pix_mp; struct mtk_q_data *q_data; int ret = 0; - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; mtk_v4l2_debug(3, "[%d]", ctx->id); @@ -925,7 +928,7 @@ static int vidioc_enum_framesizes(struct file *file, void *priv, static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool output_queue) { - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; int i, j = 0; for (i = 0; i < NUM_FORMATS; i++) { @@ -949,14 +952,14 @@ static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool output_queue) return 0; } -static int vidioc_vdec_enum_fmt_vid_cap_mplane(struct file *file, void *pirv, - struct v4l2_fmtdesc *f) +static int vidioc_vdec_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { return vidioc_enum_fmt(f, false); } -static int vidioc_vdec_enum_fmt_vid_out_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) +static int vidioc_vdec_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { return vidioc_enum_fmt(f, true); } @@ -1324,7 +1327,8 @@ static void vb2ops_vdec_stop_streaming(struct vb2_queue *q) while ((dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx))) { vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); - vb2_set_plane_payload(&dst_buf->vb2_buf, 1, 0); + if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 2) + vb2_set_plane_payload(&dst_buf->vb2_buf, 1, 0); v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); } @@ -1453,8 +1457,8 @@ const struct v4l2_ioctl_ops mtk_vdec_ioctl_ops = { .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, - .vidioc_enum_fmt_vid_cap_mplane = vidioc_vdec_enum_fmt_vid_cap_mplane, - .vidioc_enum_fmt_vid_out_mplane = vidioc_vdec_enum_fmt_vid_out_mplane, + .vidioc_enum_fmt_vid_cap = vidioc_vdec_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_out = vidioc_vdec_enum_fmt_vid_out, .vidioc_enum_framesizes = vidioc_enum_framesizes, .vidioc_querycap = vidioc_vdec_querycap, diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.h index 3861d4433be9..e0c5338bde3d 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.h +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c index 372d37824377..00d090df11bb 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c index 273f78f129da..5a6ec8fb52da 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: Tiffany Lin <tiffany.lin@mediatek.com> @@ -34,8 +34,8 @@ int mtk_vcodec_init_dec_pm(struct mtk_vcodec_dev *mtkdev) } pdev = of_find_device_by_node(node); + of_node_put(node); if (WARN_ON(!pdev)) { - of_node_put(node); return -1; } pm->larbvdec = &pdev->dev; diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.h index 74555cc5a893..872d8bf8cfaf 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.h +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: Tiffany Lin <tiffany.lin@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h index 1044176d8e6f..c95de5d08dda 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> @@ -129,7 +129,7 @@ struct mtk_q_data { enum v4l2_field field; unsigned int bytesperline[MTK_VCODEC_MAX_PLANES]; unsigned int sizeimage[MTK_VCODEC_MAX_PLANES]; - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; }; /** @@ -273,7 +273,7 @@ struct mtk_vcodec_ctx { const struct vdec_common_if *dec_if; const struct venc_common_if *enc_if; - unsigned long drv_handle; + void *drv_handle; struct vdec_pic_info picinfo; int dpb_size; diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c index 0cf5744b4c28..fd8de027e83e 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> @@ -29,7 +29,7 @@ static void mtk_venc_worker(struct work_struct *work); -static struct mtk_video_fmt mtk_video_formats[] = { +static const struct mtk_video_fmt mtk_video_formats[] = { { .fourcc = V4L2_PIX_FMT_NV12M, .type = MTK_FMT_FRAME, @@ -158,7 +158,7 @@ static const struct v4l2_ctrl_ops mtk_vcodec_enc_ctrl_ops = { static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool output_queue) { - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; int i, j = 0; for (i = 0; i < NUM_FORMATS; ++i) { @@ -199,14 +199,14 @@ static int vidioc_enum_framesizes(struct file *file, void *fh, return -EINVAL; } -static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv, - struct v4l2_fmtdesc *f) +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { return vidioc_enum_fmt(f, false); } -static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov, - struct v4l2_fmtdesc *f) +static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { return vidioc_enum_fmt(f, true); } @@ -266,9 +266,9 @@ static struct mtk_q_data *mtk_venc_get_q_data(struct mtk_vcodec_ctx *ctx, return &ctx->q_data[MTK_Q_DATA_DST]; } -static struct mtk_video_fmt *mtk_venc_find_format(struct v4l2_format *f) +static const struct mtk_video_fmt *mtk_venc_find_format(struct v4l2_format *f) { - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; unsigned int k; for (k = 0; k < NUM_FORMATS; k++) { @@ -283,7 +283,8 @@ static struct mtk_video_fmt *mtk_venc_find_format(struct v4l2_format *f) /* V4L2 specification suggests the driver corrects the format struct if any of * the dimensions is unsupported */ -static int vidioc_try_fmt(struct v4l2_format *f, struct mtk_video_fmt *fmt) +static int vidioc_try_fmt(struct v4l2_format *f, + const struct mtk_video_fmt *fmt) { struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; int i; @@ -419,7 +420,7 @@ static int vidioc_venc_s_fmt_cap(struct file *file, void *priv, struct vb2_queue *vq; struct mtk_q_data *q_data; int i, ret; - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); if (!vq) { @@ -481,7 +482,7 @@ static int vidioc_venc_s_fmt_out(struct file *file, void *priv, struct vb2_queue *vq; struct mtk_q_data *q_data; int ret, i; - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); @@ -580,7 +581,7 @@ static int vidioc_venc_g_fmt(struct file *file, void *priv, static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_format *f) { - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); fmt = mtk_venc_find_format(f); @@ -599,7 +600,7 @@ static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, struct v4l2_format *f) { - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; fmt = mtk_venc_find_format(f); if (!fmt) { @@ -717,8 +718,8 @@ const struct v4l2_ioctl_ops mtk_venc_ioctl_ops = { .vidioc_dqbuf = vidioc_venc_dqbuf, .vidioc_querycap = vidioc_venc_querycap, - .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane, - .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, .vidioc_enum_framesizes = vidioc_enum_framesizes, .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane, @@ -864,12 +865,18 @@ static int vb2ops_venc_start_streaming(struct vb2_queue *q, unsigned int count) err_set_param: for (i = 0; i < q->num_buffers; ++i) { - if (q->bufs[i]->state == VB2_BUF_STATE_ACTIVE) { + struct vb2_buffer *buf = vb2_get_buffer(q, i); + + /* + * FIXME: This check is not needed as only active buffers + * can be marked as done. + */ + if (buf->state == VB2_BUF_STATE_ACTIVE) { mtk_v4l2_debug(0, "[%d] id=%d, type=%d, %d -> VB2_BUF_STATE_QUEUED", ctx->id, i, q->type, - (int)q->bufs[i]->state); - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(q->bufs[i]), - VB2_BUF_STATE_QUEUED); + (int)buf->state); + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(buf), + VB2_BUF_STATE_QUEUED); } } diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h index 8248cb628882..a9c9f86b9c83 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c index b15e9d2ef6a9..1d82aa2b6017 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c index 4740ae5e9a8e..3e2bfded79a6 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: Tiffany Lin <tiffany.lin@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h index 63165fc1b84a..b7ecdfd74823 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: Tiffany Lin <tiffany.lin@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c index f8aae7cc5f57..a3c7a380c930 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: Tiffany Lin <tiffany.lin@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h index ba632528fa72..638cd1f3526a 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: Tiffany Lin <tiffany.lin@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c index 13f7061bfb50..d48f542db1a9 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h index 677adb990e28..b999d7b84ed1 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c index 455dbe4887c1..c5f8f1fca44c 100644 --- a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c +++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> @@ -266,7 +266,7 @@ static void get_dpb_size(struct vdec_h264_inst *inst, unsigned int *dpb_sz) mtk_vcodec_debug(inst, "sz=%d", *dpb_sz); } -static int vdec_h264_init(struct mtk_vcodec_ctx *ctx, unsigned long *h_vdec) +static int vdec_h264_init(struct mtk_vcodec_ctx *ctx) { struct vdec_h264_inst *inst = NULL; int err; @@ -295,7 +295,7 @@ static int vdec_h264_init(struct mtk_vcodec_ctx *ctx, unsigned long *h_vdec) mtk_vcodec_debug(inst, "H264 Instance >> %p", inst); - *h_vdec = (unsigned long)inst; + ctx->drv_handle = inst; return 0; error_deinit: @@ -306,7 +306,7 @@ error_free_inst: return err; } -static void vdec_h264_deinit(unsigned long h_vdec) +static void vdec_h264_deinit(void *h_vdec) { struct vdec_h264_inst *inst = (struct vdec_h264_inst *)h_vdec; @@ -331,7 +331,7 @@ static int find_start_code(unsigned char *data, unsigned int data_sz) return -1; } -static int vdec_h264_decode(unsigned long h_vdec, struct mtk_vcodec_mem *bs, +static int vdec_h264_decode(void *h_vdec, struct mtk_vcodec_mem *bs, struct vdec_fb *fb, bool *res_chg) { struct vdec_h264_inst *inst = (struct vdec_h264_inst *)h_vdec; @@ -451,8 +451,8 @@ static void vdec_h264_get_fb(struct vdec_h264_inst *inst, list->count--; } -static int vdec_h264_get_param(unsigned long h_vdec, - enum vdec_get_param_type type, void *out) +static int vdec_h264_get_param(void *h_vdec, enum vdec_get_param_type type, + void *out) { struct vdec_h264_inst *inst = (struct vdec_h264_inst *)h_vdec; @@ -485,16 +485,9 @@ static int vdec_h264_get_param(unsigned long h_vdec, return 0; } -static struct vdec_common_if vdec_h264_if = { +const struct vdec_common_if vdec_h264_if = { .init = vdec_h264_init, .decode = vdec_h264_decode, .get_param = vdec_h264_get_param, .deinit = vdec_h264_deinit, }; - -struct vdec_common_if *get_h264_dec_comm_if(void); - -struct vdec_common_if *get_h264_dec_comm_if(void) -{ - return &vdec_h264_if; -} diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c index 91139cef6283..63a8708ce682 100644 --- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c +++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: Jungchang Tsao <jungchang.tsao@mediatek.com> @@ -388,7 +388,7 @@ static void free_working_buf(struct vdec_vp8_inst *inst) inst->vsi->dec.working_buf_dma = 0; } -static int vdec_vp8_init(struct mtk_vcodec_ctx *ctx, unsigned long *h_vdec) +static int vdec_vp8_init(struct mtk_vcodec_ctx *ctx) { struct vdec_vp8_inst *inst; int err; @@ -419,7 +419,7 @@ static int vdec_vp8_init(struct mtk_vcodec_ctx *ctx, unsigned long *h_vdec) get_hw_reg_base(inst); mtk_vcodec_debug(inst, "VP8 Instance >> %p", inst); - *h_vdec = (unsigned long)inst; + ctx->drv_handle = inst; return 0; error_deinit: @@ -429,7 +429,7 @@ error_free_inst: return err; } -static int vdec_vp8_decode(unsigned long h_vdec, struct mtk_vcodec_mem *bs, +static int vdec_vp8_decode(void *h_vdec, struct mtk_vcodec_mem *bs, struct vdec_fb *fb, bool *res_chg) { struct vdec_vp8_inst *inst = (struct vdec_vp8_inst *)h_vdec; @@ -565,8 +565,8 @@ static void get_crop_info(struct vdec_vp8_inst *inst, struct v4l2_rect *cr) cr->left, cr->top, cr->width, cr->height); } -static int vdec_vp8_get_param(unsigned long h_vdec, - enum vdec_get_param_type type, void *out) +static int vdec_vp8_get_param(void *h_vdec, enum vdec_get_param_type type, + void *out) { struct vdec_vp8_inst *inst = (struct vdec_vp8_inst *)h_vdec; @@ -599,7 +599,7 @@ static int vdec_vp8_get_param(unsigned long h_vdec, return 0; } -static void vdec_vp8_deinit(unsigned long h_vdec) +static void vdec_vp8_deinit(void *h_vdec) { struct vdec_vp8_inst *inst = (struct vdec_vp8_inst *)h_vdec; @@ -610,16 +610,9 @@ static void vdec_vp8_deinit(unsigned long h_vdec) kfree(inst); } -static struct vdec_common_if vdec_vp8_if = { +const struct vdec_common_if vdec_vp8_if = { .init = vdec_vp8_init, .decode = vdec_vp8_decode, .get_param = vdec_vp8_get_param, .deinit = vdec_vp8_deinit, }; - -struct vdec_common_if *get_vp8_dec_comm_if(void); - -struct vdec_common_if *get_vp8_dec_comm_if(void) -{ - return &vdec_vp8_if; -} diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c index c1904ad5e69b..5066c283d86d 100644 --- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c +++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: Daniel Hsiao <daniel.hsiao@mediatek.com> @@ -757,7 +757,7 @@ static int validate_vsi_array_indexes(struct vdec_vp9_inst *inst, return 0; } -static void vdec_vp9_deinit(unsigned long h_vdec) +static void vdec_vp9_deinit(void *h_vdec) { struct vdec_vp9_inst *inst = (struct vdec_vp9_inst *)h_vdec; struct mtk_vcodec_mem *mem; @@ -779,7 +779,7 @@ static void vdec_vp9_deinit(unsigned long h_vdec) vp9_free_inst(inst); } -static int vdec_vp9_init(struct mtk_vcodec_ctx *ctx, unsigned long *h_vdec) +static int vdec_vp9_init(struct mtk_vcodec_ctx *ctx) { struct vdec_vp9_inst *inst; @@ -803,7 +803,7 @@ static int vdec_vp9_init(struct mtk_vcodec_ctx *ctx, unsigned long *h_vdec) inst->vsi = (struct vdec_vp9_vsi *)inst->vpu.vsi; init_all_fb_lists(inst); - (*h_vdec) = (unsigned long)inst; + ctx->drv_handle = inst; return 0; err_deinit_inst: @@ -812,8 +812,8 @@ err_deinit_inst: return -EINVAL; } -static int vdec_vp9_decode(unsigned long h_vdec, struct mtk_vcodec_mem *bs, - struct vdec_fb *fb, bool *res_chg) +static int vdec_vp9_decode(void *h_vdec, struct mtk_vcodec_mem *bs, + struct vdec_fb *fb, bool *res_chg) { int ret = 0; struct vdec_vp9_inst *inst = (struct vdec_vp9_inst *)h_vdec; @@ -969,8 +969,8 @@ static void get_crop_info(struct vdec_vp9_inst *inst, struct v4l2_rect *cr) cr->left, cr->top, cr->width, cr->height); } -static int vdec_vp9_get_param(unsigned long h_vdec, - enum vdec_get_param_type type, void *out) +static int vdec_vp9_get_param(void *h_vdec, enum vdec_get_param_type type, + void *out) { struct vdec_vp9_inst *inst = (struct vdec_vp9_inst *)h_vdec; int ret = 0; @@ -1000,16 +1000,9 @@ static int vdec_vp9_get_param(unsigned long h_vdec, return ret; } -static struct vdec_common_if vdec_vp9_if = { +const struct vdec_common_if vdec_vp9_if = { .init = vdec_vp9_init, .decode = vdec_vp9_decode, .get_param = vdec_vp9_get_param, .deinit = vdec_vp9_deinit, }; - -struct vdec_common_if *get_vp9_dec_comm_if(void); - -struct vdec_common_if *get_vp9_dec_comm_if(void) -{ - return &vdec_vp9_if; -} diff --git a/drivers/media/platform/mtk-vcodec/vdec_drv_base.h b/drivers/media/platform/mtk-vcodec/vdec_drv_base.h index b6cb922fc400..ceb4db4cb3be 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_drv_base.h +++ b/drivers/media/platform/mtk-vcodec/vdec_drv_base.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> @@ -17,7 +17,7 @@ struct vdec_common_if { * @ctx : [in] mtk v4l2 context * @h_vdec : [out] driver handle */ - int (*init)(struct mtk_vcodec_ctx *ctx, unsigned long *h_vdec); + int (*init)(struct mtk_vcodec_ctx *ctx); /** * (*decode)() - trigger decode @@ -26,7 +26,7 @@ struct vdec_common_if { * @fb : [in] frame buffer to store decoded frame * @res_chg : [out] resolution change happen */ - int (*decode)(unsigned long h_vdec, struct mtk_vcodec_mem *bs, + int (*decode)(void *h_vdec, struct mtk_vcodec_mem *bs, struct vdec_fb *fb, bool *res_chg); /** @@ -35,14 +35,14 @@ struct vdec_common_if { * @type : [in] input parameter type * @out : [out] buffer to store query result */ - int (*get_param)(unsigned long h_vdec, enum vdec_get_param_type type, + int (*get_param)(void *h_vdec, enum vdec_get_param_type type, void *out); /** * (*deinit)() - deinitialize driver. * @h_vdec : [in] driver handle to be deinit */ - void (*deinit)(unsigned long h_vdec); + void (*deinit)(void *h_vdec); }; #endif diff --git a/drivers/media/platform/mtk-vcodec/vdec_drv_if.c b/drivers/media/platform/mtk-vcodec/vdec_drv_if.c index 5c98a76a77b7..2e43dd4486e0 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_drv_if.c +++ b/drivers/media/platform/mtk-vcodec/vdec_drv_if.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> @@ -15,23 +15,19 @@ #include "mtk_vcodec_dec_pm.h" #include "mtk_vpu.h" -const struct vdec_common_if *get_h264_dec_comm_if(void); -const struct vdec_common_if *get_vp8_dec_comm_if(void); -const struct vdec_common_if *get_vp9_dec_comm_if(void); - int vdec_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc) { int ret = 0; switch (fourcc) { case V4L2_PIX_FMT_H264: - ctx->dec_if = get_h264_dec_comm_if(); + ctx->dec_if = &vdec_h264_if; break; case V4L2_PIX_FMT_VP8: - ctx->dec_if = get_vp8_dec_comm_if(); + ctx->dec_if = &vdec_vp8_if; break; case V4L2_PIX_FMT_VP9: - ctx->dec_if = get_vp9_dec_comm_if(); + ctx->dec_if = &vdec_vp9_if; break; default: return -EINVAL; @@ -39,7 +35,7 @@ int vdec_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc) mtk_vdec_lock(ctx); mtk_vcodec_dec_clock_on(&ctx->dev->pm); - ret = ctx->dec_if->init(ctx, &ctx->drv_handle); + ret = ctx->dec_if->init(ctx); mtk_vcodec_dec_clock_off(&ctx->dev->pm); mtk_vdec_unlock(ctx); @@ -66,7 +62,7 @@ int vdec_if_decode(struct mtk_vcodec_ctx *ctx, struct mtk_vcodec_mem *bs, } } - if (ctx->drv_handle == 0) + if (!ctx->drv_handle) return -EIO; mtk_vdec_lock(ctx); @@ -89,7 +85,7 @@ int vdec_if_get_param(struct mtk_vcodec_ctx *ctx, enum vdec_get_param_type type, { int ret = 0; - if (ctx->drv_handle == 0) + if (!ctx->drv_handle) return -EIO; mtk_vdec_lock(ctx); @@ -101,7 +97,7 @@ int vdec_if_get_param(struct mtk_vcodec_ctx *ctx, enum vdec_get_param_type type, void vdec_if_deinit(struct mtk_vcodec_ctx *ctx) { - if (ctx->drv_handle == 0) + if (!ctx->drv_handle) return; mtk_vdec_lock(ctx); @@ -110,5 +106,5 @@ void vdec_if_deinit(struct mtk_vcodec_ctx *ctx) mtk_vcodec_dec_clock_off(&ctx->dev->pm); mtk_vdec_unlock(ctx); - ctx->drv_handle = 0; + ctx->drv_handle = NULL; } diff --git a/drivers/media/platform/mtk-vcodec/vdec_drv_if.h b/drivers/media/platform/mtk-vcodec/vdec_drv_if.h index 409623574145..270d8dc9984b 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_drv_if.h +++ b/drivers/media/platform/mtk-vcodec/vdec_drv_if.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> @@ -54,6 +54,10 @@ struct vdec_fb_node { struct vdec_fb *fb; }; +extern const struct vdec_common_if vdec_h264_if; +extern const struct vdec_common_if vdec_vp8_if; +extern const struct vdec_common_if vdec_vp9_if; + /** * vdec_if_init() - initialize decode driver * @ctx : [in] v4l2 context diff --git a/drivers/media/platform/mtk-vcodec/vdec_ipi_msg.h b/drivers/media/platform/mtk-vcodec/vdec_ipi_msg.h index b05dcdeb7734..47a1c1c0fd04 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_ipi_msg.h +++ b/drivers/media/platform/mtk-vcodec/vdec_ipi_msg.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c index 035ba917ed0e..3f38cc4509ef 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c +++ b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h index 6701778ea5d9..b76f717e4fd7 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h +++ b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c b/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c index 3125eaf2a326..b9624f8df0e9 100644 --- a/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c +++ b/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: Jungchang Tsao <jungchang.tsao@mediatek.com> @@ -458,7 +458,7 @@ static void h264_encode_filler(struct venc_h264_inst *inst, void *buf, memset(p, 0xff, size); } -static int h264_enc_init(struct mtk_vcodec_ctx *ctx, unsigned long *handle) +static int h264_enc_init(struct mtk_vcodec_ctx *ctx) { int ret = 0; struct venc_h264_inst *inst; @@ -484,12 +484,12 @@ static int h264_enc_init(struct mtk_vcodec_ctx *ctx, unsigned long *handle) if (ret) kfree(inst); else - (*handle) = (unsigned long)inst; + ctx->drv_handle = inst; return ret; } -static int h264_enc_encode(unsigned long handle, +static int h264_enc_encode(void *handle, enum venc_start_opt opt, struct venc_frm_buf *frm_buf, struct mtk_vcodec_mem *bs_buf, @@ -584,7 +584,7 @@ encode_err: return ret; } -static int h264_enc_set_param(unsigned long handle, +static int h264_enc_set_param(void *handle, enum venc_set_param_type type, struct venc_enc_param *enc_prm) { @@ -637,7 +637,7 @@ static int h264_enc_set_param(unsigned long handle, return ret; } -static int h264_enc_deinit(unsigned long handle) +static int h264_enc_deinit(void *handle) { int ret = 0; struct venc_h264_inst *inst = (struct venc_h264_inst *)handle; @@ -655,16 +655,9 @@ static int h264_enc_deinit(unsigned long handle) return ret; } -static const struct venc_common_if venc_h264_if = { +const struct venc_common_if venc_h264_if = { .init = h264_enc_init, .encode = h264_enc_encode, .set_param = h264_enc_set_param, .deinit = h264_enc_deinit, }; - -const struct venc_common_if *get_h264_enc_comm_if(void); - -const struct venc_common_if *get_h264_enc_comm_if(void) -{ - return &venc_h264_if; -} diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c b/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c index ba19cdc4e4f1..8d36f0362efe 100644 --- a/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c +++ b/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: Daniel Hsiao <daniel.hsiao@mediatek.com> @@ -323,7 +323,7 @@ static int vp8_enc_encode_frame(struct venc_vp8_inst *inst, return ret; } -static int vp8_enc_init(struct mtk_vcodec_ctx *ctx, unsigned long *handle) +static int vp8_enc_init(struct mtk_vcodec_ctx *ctx) { int ret = 0; struct venc_vp8_inst *inst; @@ -349,12 +349,12 @@ static int vp8_enc_init(struct mtk_vcodec_ctx *ctx, unsigned long *handle) if (ret) kfree(inst); else - (*handle) = (unsigned long)inst; + ctx->drv_handle = inst; return ret; } -static int vp8_enc_encode(unsigned long handle, +static int vp8_enc_encode(void *handle, enum venc_start_opt opt, struct venc_frm_buf *frm_buf, struct mtk_vcodec_mem *bs_buf, @@ -391,7 +391,7 @@ encode_err: return ret; } -static int vp8_enc_set_param(unsigned long handle, +static int vp8_enc_set_param(void *handle, enum venc_set_param_type type, struct venc_enc_param *enc_prm) { @@ -442,7 +442,7 @@ static int vp8_enc_set_param(unsigned long handle, return ret; } -static int vp8_enc_deinit(unsigned long handle) +static int vp8_enc_deinit(void *handle) { int ret = 0; struct venc_vp8_inst *inst = (struct venc_vp8_inst *)handle; @@ -460,16 +460,9 @@ static int vp8_enc_deinit(unsigned long handle) return ret; } -static const struct venc_common_if venc_vp8_if = { +const struct venc_common_if venc_vp8_if = { .init = vp8_enc_init, .encode = vp8_enc_encode, .set_param = vp8_enc_set_param, .deinit = vp8_enc_deinit, }; - -const struct venc_common_if *get_vp8_enc_comm_if(void); - -const struct venc_common_if *get_vp8_enc_comm_if(void) -{ - return &venc_vp8_if; -} diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_base.h b/drivers/media/platform/mtk-vcodec/venc_drv_base.h index 81620683b94f..3d718411dc73 100644 --- a/drivers/media/platform/mtk-vcodec/venc_drv_base.h +++ b/drivers/media/platform/mtk-vcodec/venc_drv_base.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: Daniel Hsiao <daniel.hsiao@mediatek.com> @@ -19,7 +19,7 @@ struct venc_common_if { * @ctx: [in] mtk v4l2 context * @handle: [out] driver handle */ - int (*init)(struct mtk_vcodec_ctx *ctx, unsigned long *handle); + int (*init)(struct mtk_vcodec_ctx *ctx); /** * (*encode)() - trigger encode @@ -29,7 +29,7 @@ struct venc_common_if { * @bs_buf: [in] bitstream buffer to store output bitstream * @result: [out] encode result */ - int (*encode)(unsigned long handle, enum venc_start_opt opt, + int (*encode)(void *handle, enum venc_start_opt opt, struct venc_frm_buf *frm_buf, struct mtk_vcodec_mem *bs_buf, struct venc_done_result *result); @@ -40,14 +40,14 @@ struct venc_common_if { * @type: [in] parameter type * @in: [in] buffer to store the parameter */ - int (*set_param)(unsigned long handle, enum venc_set_param_type type, + int (*set_param)(void *handle, enum venc_set_param_type type, struct venc_enc_param *in); /** * (*deinit)() - deinitialize driver. * @handle: [in] driver handle */ - int (*deinit)(unsigned long handle); + int (*deinit)(void *handle); }; #endif diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_if.c b/drivers/media/platform/mtk-vcodec/venc_drv_if.c index 608c08b2ab8f..c6bb82ac2dcd 100644 --- a/drivers/media/platform/mtk-vcodec/venc_drv_if.c +++ b/drivers/media/platform/mtk-vcodec/venc_drv_if.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: Daniel Hsiao <daniel.hsiao@mediatek.com> @@ -17,19 +17,16 @@ #include "mtk_vcodec_enc_pm.h" #include "mtk_vpu.h" -const struct venc_common_if *get_h264_enc_comm_if(void); -const struct venc_common_if *get_vp8_enc_comm_if(void); - int venc_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc) { int ret = 0; switch (fourcc) { case V4L2_PIX_FMT_VP8: - ctx->enc_if = get_vp8_enc_comm_if(); + ctx->enc_if = &venc_vp8_if; break; case V4L2_PIX_FMT_H264: - ctx->enc_if = get_h264_enc_comm_if(); + ctx->enc_if = &venc_h264_if; break; default: return -EINVAL; @@ -37,7 +34,7 @@ int venc_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc) mtk_venc_lock(ctx); mtk_vcodec_enc_clock_on(&ctx->dev->pm); - ret = ctx->enc_if->init(ctx, (unsigned long *)&ctx->drv_handle); + ret = ctx->enc_if->init(ctx); mtk_vcodec_enc_clock_off(&ctx->dev->pm); mtk_venc_unlock(ctx); @@ -89,7 +86,7 @@ int venc_if_deinit(struct mtk_vcodec_ctx *ctx) { int ret = 0; - if (ctx->drv_handle == 0) + if (!ctx->drv_handle) return 0; mtk_venc_lock(ctx); @@ -98,7 +95,7 @@ int venc_if_deinit(struct mtk_vcodec_ctx *ctx) mtk_vcodec_enc_clock_off(&ctx->dev->pm); mtk_venc_unlock(ctx); - ctx->drv_handle = 0; + ctx->drv_handle = NULL; return ret; } diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_if.h b/drivers/media/platform/mtk-vcodec/venc_drv_if.h index bbba1cec7be4..52fc9cc812fc 100644 --- a/drivers/media/platform/mtk-vcodec/venc_drv_if.h +++ b/drivers/media/platform/mtk-vcodec/venc_drv_if.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: Daniel Hsiao <daniel.hsiao@mediatek.com> @@ -110,6 +110,9 @@ struct venc_done_result { bool is_key_frm; }; +extern const struct venc_common_if venc_h264_if; +extern const struct venc_common_if venc_vp8_if; + /* * venc_if_init - Create the driver handle * @ctx: device context diff --git a/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h b/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h index be34780760f4..28ee04ca6241 100644 --- a/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h +++ b/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: Jungchang Tsao <jungchang.tsao@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/venc_vpu_if.c b/drivers/media/platform/mtk-vcodec/venc_vpu_if.c index 7daf8694c62e..3e931b0ed096 100644 --- a/drivers/media/platform/mtk-vcodec/venc_vpu_if.c +++ b/drivers/media/platform/mtk-vcodec/venc_vpu_if.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: PoChun Lin <pochun.lin@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/venc_vpu_if.h b/drivers/media/platform/mtk-vcodec/venc_vpu_if.h index a6b6d0eafb50..ba301a138a5a 100644 --- a/drivers/media/platform/mtk-vcodec/venc_vpu_if.h +++ b/drivers/media/platform/mtk-vcodec/venc_vpu_if.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: PoChun Lin <pochun.lin@mediatek.com> diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.c b/drivers/media/platform/mtk-vpu/mtk_vpu.c index da655d166d52..cc2ff40d060d 100644 --- a/drivers/media/platform/mtk-vpu/mtk_vpu.c +++ b/drivers/media/platform/mtk-vpu/mtk_vpu.c @@ -460,9 +460,9 @@ struct platform_device *vpu_get_plat_device(struct platform_device *pdev) } vpu_pdev = of_find_device_by_node(vpu_node); + of_node_put(vpu_node); if (WARN_ON(!vpu_pdev)) { dev_err(dev, "vpu pdev failed\n"); - of_node_put(vpu_node); return NULL; } diff --git a/drivers/media/platform/omap/Kconfig b/drivers/media/platform/omap/Kconfig index 08a606a5adff..1a99dff21ca0 100644 --- a/drivers/media/platform/omap/Kconfig +++ b/drivers/media/platform/omap/Kconfig @@ -14,6 +14,5 @@ config VIDEO_OMAP2_VOUT select VIDEOBUF_DMA_CONTIG select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3 select FRAME_VECTOR - default n help V4L2 Display driver support for OMAP2/3 based boards. diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 38849f0ba09d..83216fc7156b 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -2003,6 +2003,8 @@ static int isp_remove(struct platform_device *pdev) media_entity_enum_cleanup(&isp->crashed); v4l2_async_notifier_cleanup(&isp->notifier); + kfree(isp); + return 0; } @@ -2193,7 +2195,7 @@ static int isp_probe(struct platform_device *pdev) int ret; int i, m; - isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL); + isp = kzalloc(sizeof(*isp), GFP_KERNEL); if (!isp) { dev_err(&pdev->dev, "could not allocate memory\n"); return -ENOMEM; @@ -2202,17 +2204,19 @@ static int isp_probe(struct platform_device *pdev) ret = fwnode_property_read_u32(of_fwnode_handle(pdev->dev.of_node), "ti,phy-type", &isp->phy_type); if (ret) - return ret; + goto error_release_isp; isp->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "syscon"); - if (IS_ERR(isp->syscon)) - return PTR_ERR(isp->syscon); + if (IS_ERR(isp->syscon)) { + ret = PTR_ERR(isp->syscon); + goto error_release_isp; + } ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, &isp->syscon_offset); if (ret) - return ret; + goto error_release_isp; isp->autoidle = autoidle; @@ -2369,6 +2373,8 @@ error_isp: error: v4l2_async_notifier_cleanup(&isp->notifier); mutex_destroy(&isp->isp_mutex); +error_release_isp: + kfree(isp); return ret; } @@ -2380,7 +2386,7 @@ static const struct dev_pm_ops omap3isp_pm_ops = { .complete = isp_pm_complete, }; -static struct platform_device_id omap3isp_id_table[] = { +static const struct platform_device_id omap3isp_id_table[] = { { "omap3isp", 0 }, { }, }; diff --git a/drivers/media/platform/omap3isp/isph3a_aewb.c b/drivers/media/platform/omap3isp/isph3a_aewb.c index e27c502ffa4a..e6c54c4bbfca 100644 --- a/drivers/media/platform/omap3isp/isph3a_aewb.c +++ b/drivers/media/platform/omap3isp/isph3a_aewb.c @@ -288,9 +288,10 @@ int omap3isp_h3a_aewb_init(struct isp_device *isp) { struct ispstat *aewb = &isp->isp_aewb; struct omap3isp_h3a_aewb_config *aewb_cfg; - struct omap3isp_h3a_aewb_config *aewb_recover_cfg; + struct omap3isp_h3a_aewb_config *aewb_recover_cfg = NULL; + int ret; - aewb_cfg = devm_kzalloc(isp->dev, sizeof(*aewb_cfg), GFP_KERNEL); + aewb_cfg = kzalloc(sizeof(*aewb_cfg), GFP_KERNEL); if (!aewb_cfg) return -ENOMEM; @@ -300,12 +301,12 @@ int omap3isp_h3a_aewb_init(struct isp_device *isp) aewb->isp = isp; /* Set recover state configuration */ - aewb_recover_cfg = devm_kzalloc(isp->dev, sizeof(*aewb_recover_cfg), - GFP_KERNEL); + aewb_recover_cfg = kzalloc(sizeof(*aewb_recover_cfg), GFP_KERNEL); if (!aewb_recover_cfg) { dev_err(aewb->isp->dev, "AEWB: cannot allocate memory for recover configuration.\n"); - return -ENOMEM; + ret = -ENOMEM; + goto err; } aewb_recover_cfg->saturation_limit = OMAP3ISP_AEWB_MAX_SATURATION_LIM; @@ -322,13 +323,22 @@ int omap3isp_h3a_aewb_init(struct isp_device *isp) if (h3a_aewb_validate_params(aewb, aewb_recover_cfg)) { dev_err(aewb->isp->dev, "AEWB: recover configuration is invalid.\n"); - return -EINVAL; + ret = -EINVAL; + goto err; } aewb_recover_cfg->buf_size = h3a_aewb_get_buf_size(aewb_recover_cfg); aewb->recover_priv = aewb_recover_cfg; - return omap3isp_stat_init(aewb, "AEWB", &h3a_aewb_subdev_ops); + ret = omap3isp_stat_init(aewb, "AEWB", &h3a_aewb_subdev_ops); + +err: + if (ret) { + kfree(aewb_cfg); + kfree(aewb_recover_cfg); + } + + return ret; } /* diff --git a/drivers/media/platform/omap3isp/isph3a_af.c b/drivers/media/platform/omap3isp/isph3a_af.c index 4f61776abc20..a65cfdfa9637 100644 --- a/drivers/media/platform/omap3isp/isph3a_af.c +++ b/drivers/media/platform/omap3isp/isph3a_af.c @@ -351,9 +351,10 @@ int omap3isp_h3a_af_init(struct isp_device *isp) { struct ispstat *af = &isp->isp_af; struct omap3isp_h3a_af_config *af_cfg; - struct omap3isp_h3a_af_config *af_recover_cfg; + struct omap3isp_h3a_af_config *af_recover_cfg = NULL; + int ret; - af_cfg = devm_kzalloc(isp->dev, sizeof(*af_cfg), GFP_KERNEL); + af_cfg = kzalloc(sizeof(*af_cfg), GFP_KERNEL); if (af_cfg == NULL) return -ENOMEM; @@ -363,12 +364,12 @@ int omap3isp_h3a_af_init(struct isp_device *isp) af->isp = isp; /* Set recover state configuration */ - af_recover_cfg = devm_kzalloc(isp->dev, sizeof(*af_recover_cfg), - GFP_KERNEL); + af_recover_cfg = kzalloc(sizeof(*af_recover_cfg), GFP_KERNEL); if (!af_recover_cfg) { dev_err(af->isp->dev, "AF: cannot allocate memory for recover configuration.\n"); - return -ENOMEM; + ret = -ENOMEM; + goto err; } af_recover_cfg->paxel.h_start = OMAP3ISP_AF_PAXEL_HZSTART_MIN; @@ -380,13 +381,22 @@ int omap3isp_h3a_af_init(struct isp_device *isp) if (h3a_af_validate_params(af, af_recover_cfg)) { dev_err(af->isp->dev, "AF: recover configuration is invalid.\n"); - return -EINVAL; + ret = -EINVAL; + goto err; } af_recover_cfg->buf_size = h3a_af_get_buf_size(af_recover_cfg); af->recover_priv = af_recover_cfg; - return omap3isp_stat_init(af, "AF", &h3a_af_subdev_ops); + ret = omap3isp_stat_init(af, "AF", &h3a_af_subdev_ops); + +err: + if (ret) { + kfree(af_cfg); + kfree(af_recover_cfg); + } + + return ret; } void omap3isp_h3a_af_cleanup(struct isp_device *isp) diff --git a/drivers/media/platform/omap3isp/isphist.c b/drivers/media/platform/omap3isp/isphist.c index e36571b355f6..0ef78aace6da 100644 --- a/drivers/media/platform/omap3isp/isphist.c +++ b/drivers/media/platform/omap3isp/isphist.c @@ -475,9 +475,9 @@ int omap3isp_hist_init(struct isp_device *isp) { struct ispstat *hist = &isp->isp_hist; struct omap3isp_hist_config *hist_cfg; - int ret = -1; + int ret; - hist_cfg = devm_kzalloc(isp->dev, sizeof(*hist_cfg), GFP_KERNEL); + hist_cfg = kzalloc(sizeof(*hist_cfg), GFP_KERNEL); if (hist_cfg == NULL) return -ENOMEM; @@ -499,7 +499,7 @@ int omap3isp_hist_init(struct isp_device *isp) if (IS_ERR(hist->dma_ch)) { ret = PTR_ERR(hist->dma_ch); if (ret == -EPROBE_DEFER) - return ret; + goto err; hist->dma_ch = NULL; dev_warn(isp->dev, @@ -515,9 +515,12 @@ int omap3isp_hist_init(struct isp_device *isp) hist->event_type = V4L2_EVENT_OMAP3ISP_HIST; ret = omap3isp_stat_init(hist, "histogram", &hist_subdev_ops); + +err: if (ret) { - if (hist->dma_ch) + if (!IS_ERR_OR_NULL(hist->dma_ch)) dma_release_channel(hist->dma_ch); + kfree(hist_cfg); } return ret; diff --git a/drivers/media/platform/omap3isp/ispstat.c b/drivers/media/platform/omap3isp/ispstat.c index ca7bb8497c3d..62b2eacb96fd 100644 --- a/drivers/media/platform/omap3isp/ispstat.c +++ b/drivers/media/platform/omap3isp/ispstat.c @@ -1037,7 +1037,7 @@ static int isp_stat_init_entities(struct ispstat *stat, const char *name, v4l2_subdev_init(subdev, sd_ops); snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "OMAP3 ISP %s", name); - subdev->grp_id = 1 << 16; /* group ID for isp subdevs */ + subdev->grp_id = BIT(16); /* group ID for isp subdevs */ subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; v4l2_set_subdevdata(subdev, stat); @@ -1075,4 +1075,6 @@ void omap3isp_stat_cleanup(struct ispstat *stat) mutex_destroy(&stat->ioctl_lock); isp_stat_bufs_free(stat); kfree(stat->buf); + kfree(stat->priv); + kfree(stat->recover_priv); } diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index 6bb4dd264b71..499a7284c5a8 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -1492,6 +1492,5 @@ int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev) void omap3isp_video_unregister(struct isp_video *video) { - if (video_is_registered(&video->video)) - video_unregister_device(&video->video); + video_unregister_device(&video->video); } diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index 6addc5ea8494..1c9bfaabc54c 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -1388,7 +1388,7 @@ static int pxa_buffer_init(struct pxa_camera_dev *pcdev, break; default: return -EINVAL; - }; + } buf->nb_planes = nb_channels; ret = sg_split(sgt->sgl, sgt->nents, 0, nb_channels, diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 58aebe7114cd..1d50dfbbb762 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -703,7 +703,7 @@ static int video_s_input(struct file *file, void *fh, unsigned int input) static const struct v4l2_ioctl_ops msm_vid_ioctl_ops = { .vidioc_querycap = video_querycap, - .vidioc_enum_fmt_vid_cap_mplane = video_enum_fmt, + .vidioc_enum_fmt_vid_cap = video_enum_fmt, .vidioc_g_fmt_vid_cap_mplane = video_g_fmt, .vidioc_s_fmt_vid_cap_mplane = video_s_fmt, .vidioc_try_fmt_vid_cap_mplane = video_try_fmt, diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index db8e40b55d72..0acc7576cc58 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -446,7 +446,7 @@ static const struct venus_resources msm8996_res = { .reg_tbl_size = ARRAY_SIZE(msm8996_reg_preset), .clks = {"core", "iface", "bus", "mbus" }, .clks_num = 4, - .max_load = 3110400, /* 4096x2160@90 */ + .max_load = 2563200, .hfi_version = HFI_VERSION_3XX, .vmem_id = VIDC_RESOURCE_NONE, .vmem_size = 0, @@ -469,7 +469,7 @@ static const struct venus_resources sdm845_res = { .freq_tbl_size = ARRAY_SIZE(sdm845_freq_table), .clks = {"core", "iface", "bus" }, .clks_num = 3, - .max_load = 2563200, + .max_load = 3110400, /* 4096x2160@90 */ .hfi_version = HFI_VERSION_4XX, .vmem_id = VIDC_RESOURCE_NONE, .vmem_size = 0, diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index 1eba23409ff3..d3d1748a7ef6 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -78,11 +78,11 @@ static int venus_load_fw(struct venus_core *core, const char *fwname, ret = of_address_to_resource(node, 0, &r); if (ret) - return ret; + goto err_put_node; ret = request_firmware(&mdt, fwname, dev); if (ret < 0) - return ret; + goto err_put_node; fw_size = qcom_mdt_get_size(mdt); if (fw_size < 0) { @@ -116,6 +116,8 @@ static int venus_load_fw(struct venus_core *core, const char *fwname, memunmap(mem_va); err_release_fw: release_firmware(mdt); +err_put_node: + of_node_put(node); return ret; } diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 7d0017613113..71b06dfc6dc4 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -458,6 +458,13 @@ static bool is_dynamic_bufmode(struct venus_inst *inst) struct venus_core *core = inst->core; struct venus_caps *caps; + /* + * v4 doesn't send BUFFER_ALLOC_MODE_SUPPORTED property and supports + * dynamic buffer mode by default for HFI_BUFFER_OUTPUT/OUTPUT2. + */ + if (IS_V4(core)) + return true; + caps = venus_caps_by_codec(core, inst->hfi_codec, inst->session_type); if (!caps) return false; diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c index 8efd55a2ad70..4f645076abfb 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.c +++ b/drivers/media/platform/qcom/venus/hfi_cmds.c @@ -1205,6 +1205,8 @@ pkt_session_set_property_4xx(struct hfi_session_set_property_pkt *pkt, break; } case HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE: + case HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER: + case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE: /* not implemented on Venus 4xx */ return -ENOTSUPP; default: diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 6205ad8b3201..e1f998656c07 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -482,8 +482,8 @@ unlock: static const struct v4l2_ioctl_ops vdec_ioctl_ops = { .vidioc_querycap = vdec_querycap, - .vidioc_enum_fmt_vid_cap_mplane = vdec_enum_fmt, - .vidioc_enum_fmt_vid_out_mplane = vdec_enum_fmt, + .vidioc_enum_fmt_vid_cap = vdec_enum_fmt, + .vidioc_enum_fmt_vid_out = vdec_enum_fmt, .vidioc_s_fmt_vid_cap_mplane = vdec_s_fmt, .vidioc_s_fmt_vid_out_mplane = vdec_s_fmt, .vidioc_g_fmt_vid_cap_mplane = vdec_g_fmt, diff --git a/drivers/media/platform/qcom/venus/vdec_ctrls.c b/drivers/media/platform/qcom/venus/vdec_ctrls.c index 68e0f7d0b8fc..300350bfe8bd 100644 --- a/drivers/media/platform/qcom/venus/vdec_ctrls.c +++ b/drivers/media/platform/qcom/venus/vdec_ctrls.c @@ -66,7 +66,7 @@ static int vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl) break; default: return -EINVAL; - }; + } return 0; } diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 7a4815d52c12..a5f3d2c46bea 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -607,8 +607,8 @@ static int venc_enum_frameintervals(struct file *file, void *fh, static const struct v4l2_ioctl_ops venc_ioctl_ops = { .vidioc_querycap = venc_querycap, - .vidioc_enum_fmt_vid_cap_mplane = venc_enum_fmt, - .vidioc_enum_fmt_vid_out_mplane = venc_enum_fmt, + .vidioc_enum_fmt_vid_cap = venc_enum_fmt, + .vidioc_enum_fmt_vid_out = venc_enum_fmt, .vidioc_s_fmt_vid_cap_mplane = venc_s_fmt, .vidioc_s_fmt_vid_out_mplane = venc_s_fmt, .vidioc_g_fmt_vid_cap_mplane = venc_g_fmt, diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c index 8832285d8c15..877c0b3299e9 100644 --- a/drivers/media/platform/qcom/venus/venc_ctrls.c +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c @@ -108,6 +108,9 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_H264_PROFILE: ctr->profile.h264 = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: + ctr->profile.hevc = ctrl->val; + break; case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: ctr->profile.vpx = ctrl->val; break; @@ -117,6 +120,9 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_H264_LEVEL: ctr->level.h264 = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: + ctr->level.hevc = ctrl->val; + break; case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: ctr->h264_i_qp = ctrl->val; break; @@ -208,7 +214,7 @@ int venc_ctrl_init(struct venus_inst *inst) { int ret; - ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 28); + ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 30); if (ret) return ret; @@ -237,6 +243,19 @@ int venc_ctrl_init(struct venus_inst *inst) 0, V4L2_MPEG_VIDEO_MPEG4_LEVEL_0); v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10, + ~((1 << V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) | + (1 << V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE) | + (1 << V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10)), + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, + V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2, + 0, V4L2_MPEG_VIDEO_HEVC_LEVEL_1); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_PROFILE, V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH, ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | @@ -265,7 +284,7 @@ int venc_ctrl_init(struct venus_inst *inst) v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, - V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES, + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES, 0, V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE); v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c index 8f097e514900..c14af1b929df 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -1019,10 +1019,8 @@ static int rcsi2_probe_resources(struct rcar_csi2 *priv, return ret; priv->rstc = devm_reset_control_get(&pdev->dev, NULL); - if (IS_ERR(priv->rstc)) - return PTR_ERR(priv->rstc); - return 0; + return PTR_ERR_OR_ZERO(priv->rstc); } static const struct rcar_csi2_info rcar_csi2_info_r8a7795 = { diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index 7cbdcbf9b090..0936bcd98df1 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -749,103 +749,65 @@ static const struct v4l2_ioctl_ops rvin_mc_ioctl_ops = { * File Operations */ -static int rvin_power_on(struct rvin_dev *vin) +static int rvin_power_parallel(struct rvin_dev *vin, bool on) { - int ret; struct v4l2_subdev *sd = vin_to_source(vin); - - pm_runtime_get_sync(vin->v4l2_dev.dev); - - ret = v4l2_subdev_call(sd, core, s_power, 1); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) - return ret; - return 0; -} - -static int rvin_power_off(struct rvin_dev *vin) -{ + int power = on ? 1 : 0; int ret; - struct v4l2_subdev *sd = vin_to_source(vin); - - ret = v4l2_subdev_call(sd, core, s_power, 0); - - pm_runtime_put(vin->v4l2_dev.dev); + ret = v4l2_subdev_call(sd, core, s_power, power); if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) return ret; return 0; } -static int rvin_initialize_device(struct file *file) +static int rvin_open(struct file *file) { struct rvin_dev *vin = video_drvdata(file); int ret; - struct v4l2_format f = { - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - .fmt.pix = { - .width = vin->format.width, - .height = vin->format.height, - .field = vin->format.field, - .colorspace = vin->format.colorspace, - .pixelformat = vin->format.pixelformat, - }, - }; - - ret = rvin_power_on(vin); + ret = pm_runtime_get_sync(vin->dev); if (ret < 0) return ret; - pm_runtime_enable(&vin->vdev.dev); - ret = pm_runtime_resume(&vin->vdev.dev); - if (ret < 0 && ret != -ENOSYS) - goto eresume; - - /* - * Try to configure with default parameters. Notice: this is the - * very first open, so, we cannot race against other calls, - * apart from someone else calling open() simultaneously, but - * .host_lock is protecting us against it. - */ - ret = rvin_s_fmt_vid_cap(file, NULL, &f); - if (ret < 0) - goto esfmt; - - v4l2_ctrl_handler_setup(&vin->ctrl_handler); - - return 0; -esfmt: - pm_runtime_disable(&vin->vdev.dev); -eresume: - rvin_power_off(vin); - - return ret; -} - -static int rvin_open(struct file *file) -{ - struct rvin_dev *vin = video_drvdata(file); - int ret; - - mutex_lock(&vin->lock); + ret = mutex_lock_interruptible(&vin->lock); + if (ret) + goto err_pm; file->private_data = vin; ret = v4l2_fh_open(file); if (ret) - goto unlock; - - if (!v4l2_fh_is_singular_file(file)) - goto unlock; + goto err_unlock; - if (rvin_initialize_device(file)) { - v4l2_fh_release(file); - ret = -ENODEV; + if (vin->info->use_mc) { + ret = v4l2_pipeline_pm_use(&vin->vdev.entity, 1); + if (ret < 0) + goto err_open; + } else { + if (v4l2_fh_is_singular_file(file)) { + ret = rvin_power_parallel(vin, true); + if (ret < 0) + goto err_open; + + ret = v4l2_ctrl_handler_setup(&vin->ctrl_handler); + if (ret) + goto err_parallel; + } } + mutex_unlock(&vin->lock); -unlock: + return 0; +err_parallel: + rvin_power_parallel(vin, false); +err_open: + v4l2_fh_release(file); +err_unlock: mutex_unlock(&vin->lock); +err_pm: + pm_runtime_put(vin->dev); + return ret; } @@ -863,18 +825,17 @@ static int rvin_release(struct file *file) /* the release helper will cleanup any on-going streaming */ ret = _vb2_fop_release(file, NULL); - /* - * If this was the last open file. - * Then de-initialize hw module. - */ - if (fh_singular) { - pm_runtime_suspend(&vin->vdev.dev); - pm_runtime_disable(&vin->vdev.dev); - rvin_power_off(vin); + if (vin->info->use_mc) { + v4l2_pipeline_pm_use(&vin->vdev.entity, 0); + } else { + if (fh_singular) + rvin_power_parallel(vin, false); } mutex_unlock(&vin->lock); + pm_runtime_put(vin->dev); + return ret; } @@ -888,74 +849,6 @@ static const struct v4l2_file_operations rvin_fops = { .read = vb2_fop_read, }; -/* ----------------------------------------------------------------------------- - * Media controller file operations - */ - -static int rvin_mc_open(struct file *file) -{ - struct rvin_dev *vin = video_drvdata(file); - int ret; - - ret = mutex_lock_interruptible(&vin->lock); - if (ret) - return ret; - - ret = pm_runtime_get_sync(vin->dev); - if (ret < 0) - goto err_unlock; - - ret = v4l2_pipeline_pm_use(&vin->vdev.entity, 1); - if (ret < 0) - goto err_pm; - - file->private_data = vin; - - ret = v4l2_fh_open(file); - if (ret) - goto err_v4l2pm; - - mutex_unlock(&vin->lock); - - return 0; -err_v4l2pm: - v4l2_pipeline_pm_use(&vin->vdev.entity, 0); -err_pm: - pm_runtime_put(vin->dev); -err_unlock: - mutex_unlock(&vin->lock); - - return ret; -} - -static int rvin_mc_release(struct file *file) -{ - struct rvin_dev *vin = video_drvdata(file); - int ret; - - mutex_lock(&vin->lock); - - /* the release helper will cleanup any on-going streaming. */ - ret = _vb2_fop_release(file, NULL); - - v4l2_pipeline_pm_use(&vin->vdev.entity, 0); - pm_runtime_put(vin->dev); - - mutex_unlock(&vin->lock); - - return ret; -} - -static const struct v4l2_file_operations rvin_mc_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = video_ioctl2, - .open = rvin_mc_open, - .release = rvin_mc_release, - .poll = vb2_fop_poll, - .mmap = vb2_fop_mmap, - .read = vb2_fop_read, -}; - void rvin_v4l2_unregister(struct rvin_dev *vin) { if (!video_is_registered(&vin->vdev)) @@ -996,6 +889,7 @@ int rvin_v4l2_register(struct rvin_dev *vin) snprintf(vdev->name, sizeof(vdev->name), "VIN%u output", vin->id); vdev->release = video_device_release_empty; vdev->lock = &vin->lock; + vdev->fops = &rvin_fops; vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; @@ -1007,10 +901,8 @@ int rvin_v4l2_register(struct rvin_dev *vin) vin->format.colorspace = RVIN_DEFAULT_COLORSPACE; if (vin->info->use_mc) { - vdev->fops = &rvin_mc_fops; vdev->ioctl_ops = &rvin_mc_ioctl_ops; } else { - vdev->fops = &rvin_fops; vdev->ioctl_ops = &rvin_ioctl_ops; rvin_reset_format(vin); } diff --git a/drivers/media/platform/rcar_fdp1.c b/drivers/media/platform/rcar_fdp1.c index 6a90bc4c476e..43aae9b6bb20 100644 --- a/drivers/media/platform/rcar_fdp1.c +++ b/drivers/media/platform/rcar_fdp1.c @@ -257,6 +257,8 @@ MODULE_PARM_DESC(debug, "activate debug info"); #define FD1_IP_H3_ES1 0x02010101 #define FD1_IP_M3W 0x02010202 #define FD1_IP_H3 0x02010203 +#define FD1_IP_M3N 0x02010204 +#define FD1_IP_E3 0x02010205 /* LUTs */ #define FD1_LUT_DIF_ADJ 0x1000 @@ -1730,8 +1732,8 @@ static const char * const fdp1_ctrl_deint_menu[] = { static const struct v4l2_ioctl_ops fdp1_ioctl_ops = { .vidioc_querycap = fdp1_vidioc_querycap, - .vidioc_enum_fmt_vid_cap_mplane = fdp1_enum_fmt_vid_cap, - .vidioc_enum_fmt_vid_out_mplane = fdp1_enum_fmt_vid_out, + .vidioc_enum_fmt_vid_cap = fdp1_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_out = fdp1_enum_fmt_vid_out, .vidioc_g_fmt_vid_cap_mplane = fdp1_g_fmt, .vidioc_g_fmt_vid_out_mplane = fdp1_g_fmt, .vidioc_try_fmt_vid_cap_mplane = fdp1_try_fmt, @@ -2365,6 +2367,12 @@ static int fdp1_probe(struct platform_device *pdev) case FD1_IP_H3: dprintk(fdp1, "FDP1 Version R-Car H3\n"); break; + case FD1_IP_M3N: + dprintk(fdp1, "FDP1 Version R-Car M3N\n"); + break; + case FD1_IP_E3: + dprintk(fdp1, "FDP1 Version R-Car E3\n"); + break; default: dev_err(fdp1->dev, "FDP1 Unidentifiable (0x%08x)\n", hw_version); diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c index 1dfd2eb65920..1c3f507acfc9 100644 --- a/drivers/media/platform/rcar_jpu.c +++ b/drivers/media/platform/rcar_jpu.c @@ -671,8 +671,6 @@ static int jpu_querycap(struct file *file, void *priv, strscpy(cap->driver, DRV_NAME, sizeof(cap->driver)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev_name(ctx->jpu->dev)); - cap->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; - cap->capabilities = V4L2_CAP_DEVICE_CAPS | cap->device_caps; memset(cap->reserved, 0, sizeof(cap->reserved)); return 0; @@ -948,8 +946,8 @@ static int jpu_streamon(struct file *file, void *priv, enum v4l2_buf_type type) static const struct v4l2_ioctl_ops jpu_ioctl_ops = { .vidioc_querycap = jpu_querycap, - .vidioc_enum_fmt_vid_cap_mplane = jpu_enum_fmt_cap, - .vidioc_enum_fmt_vid_out_mplane = jpu_enum_fmt_out, + .vidioc_enum_fmt_vid_cap = jpu_enum_fmt_cap, + .vidioc_enum_fmt_vid_out = jpu_enum_fmt_out, .vidioc_g_fmt_vid_cap_mplane = jpu_g_fmt, .vidioc_g_fmt_vid_out_mplane = jpu_g_fmt, .vidioc_try_fmt_vid_cap_mplane = jpu_try_fmt, @@ -1662,6 +1660,8 @@ static int jpu_probe(struct platform_device *pdev) jpu->vfd_encoder.lock = &jpu->mutex; jpu->vfd_encoder.v4l2_dev = &jpu->v4l2_dev; jpu->vfd_encoder.vfl_dir = VFL_DIR_M2M; + jpu->vfd_encoder.device_caps = V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_M2M_MPLANE; ret = video_register_device(&jpu->vfd_encoder, VFL_TYPE_GRABBER, -1); if (ret) { @@ -1679,6 +1679,8 @@ static int jpu_probe(struct platform_device *pdev) jpu->vfd_decoder.lock = &jpu->mutex; jpu->vfd_decoder.v4l2_dev = &jpu->v4l2_dev; jpu->vfd_decoder.vfl_dir = VFL_DIR_M2M; + jpu->vfd_decoder.device_caps = V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_M2M_MPLANE; ret = video_register_device(&jpu->vfd_decoder, VFL_TYPE_GRABBER, -1); if (ret) { diff --git a/drivers/media/platform/renesas-ceu.c b/drivers/media/platform/renesas-ceu.c index 150196f7cf96..57d0c0f9fa4b 100644 --- a/drivers/media/platform/renesas-ceu.c +++ b/drivers/media/platform/renesas-ceu.c @@ -1339,7 +1339,7 @@ static int ceu_enum_frameintervals(struct file *file, void *fh, static const struct v4l2_ioctl_ops ceu_ioctl_ops = { .vidioc_querycap = ceu_querycap, - .vidioc_enum_fmt_vid_cap_mplane = ceu_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = ceu_enum_fmt_vid_cap, .vidioc_try_fmt_vid_cap_mplane = ceu_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap_mplane = ceu_s_fmt_vid_cap, .vidioc_g_fmt_vid_cap_mplane = ceu_g_fmt_vid_cap, diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 4e936b95018a..b776f83e395e 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -523,7 +523,8 @@ static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx, dev); ctx->mv_count = s5p_mfc_hw_call(dev->mfc_ops, get_mv_count, dev); - ctx->scratch_buf_size = s5p_mfc_hw_call(dev->mfc_ops, + if (FW_HAS_E_MIN_SCRATCH_BUF(dev)) + ctx->scratch_buf_size = s5p_mfc_hw_call(dev->mfc_ops, get_min_scratch_buf_size, dev); if (ctx->img_width == 0 || ctx->img_height == 0) ctx->state = MFCINST_ERROR; @@ -1344,6 +1345,7 @@ static int s5p_mfc_probe(struct platform_device *pdev) vfd->lock = &dev->mfc_mutex; vfd->v4l2_dev = &dev->v4l2_dev; vfd->vfl_dir = VFL_DIR_M2M; + vfd->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; set_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags); snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_DEC_NAME); dev->vfd_dec = vfd; @@ -1362,6 +1364,7 @@ static int s5p_mfc_probe(struct platform_device *pdev) vfd->lock = &dev->mfc_mutex; vfd->v4l2_dev = &dev->v4l2_dev; vfd->vfl_dir = VFL_DIR_M2M; + vfd->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_ENC_NAME); dev->vfd_enc = vfd; video_set_drvdata(vfd, dev); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index d12fc4f397b6..4017c8b471f4 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -271,13 +271,6 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(cap->card, dev->vfd_dec->name, sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev_name(&dev->plat_dev->dev)); - /* - * This is only a mem-to-mem video device. The capture and output - * device capability flags are left only for backward compatibility - * and are scheduled for removal. - */ - cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -309,14 +302,14 @@ static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f, return 0; } -static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv, - struct v4l2_fmtdesc *f) +static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv, + struct v4l2_fmtdesc *f) { return vidioc_enum_fmt(file, f, false); } -static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) +static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { return vidioc_enum_fmt(file, f, true); } @@ -883,8 +876,8 @@ static int vidioc_subscribe_event(struct v4l2_fh *fh, /* v4l2_ioctl_ops */ static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = { .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane, - .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt, .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt, .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt, diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index 74090a68f807..97e76480e942 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -130,7 +130,7 @@ static struct mfc_control controls[] = { .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, .type = V4L2_CTRL_TYPE_MENU, .minimum = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE, - .maximum = V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES, + .maximum = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES, .default_value = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE, .menu_skip_mask = 0, }, @@ -1313,13 +1313,6 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(cap->card, dev->vfd_enc->name, sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev_name(&dev->plat_dev->dev)); - /* - * This is only a mem-to-mem video device. The capture and output - * device capability flags are left only for backward compatibility - * and are scheduled for removal. - */ - cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1350,14 +1343,14 @@ static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f, return -EINVAL; } -static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv, - struct v4l2_fmtdesc *f) +static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv, + struct v4l2_fmtdesc *f) { return vidioc_enum_fmt(file, f, false); } -static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov, - struct v4l2_fmtdesc *f) +static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { return vidioc_enum_fmt(file, f, true); } @@ -2339,8 +2332,8 @@ static int vidioc_subscribe_event(struct v4l2_fh *fh, static const struct v4l2_ioctl_ops s5p_mfc_enc_ioctl_ops = { .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane, - .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt, .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt, .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt, diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c index ee727e21ef5b..f76a07400966 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c @@ -692,9 +692,9 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx) /* multi-slice control */ /* multi-slice MB number or bit size */ mfc_write(dev, p->slice_mode, S5P_FIMV_ENC_MSLICE_CTRL); - if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) { + if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB) { mfc_write(dev, p->slice_mb, S5P_FIMV_ENC_MSLICE_MB); - } else if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) { + } else if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES) { mfc_write(dev, p->slice_bit, S5P_FIMV_ENC_MSLICE_BIT); } else { mfc_write(dev, 0, S5P_FIMV_ENC_MSLICE_MB); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index 8717b475d58d..f7621a9051cb 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -733,10 +733,10 @@ static int s5p_mfc_set_slice_mode(struct s5p_mfc_ctx *ctx) /* multi-slice control */ /* multi-slice MB number or bit size */ writel(ctx->slice_mode, mfc_regs->e_mslice_mode); - if (ctx->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) { + if (ctx->slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB) { writel(ctx->slice_size.mb, mfc_regs->e_mslice_size_mb); } else if (ctx->slice_mode == - V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) { + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES) { writel(ctx->slice_size.bits, mfc_regs->e_mslice_size_bits); } else { writel(0x0, mfc_regs->e_mslice_size_mb); @@ -776,11 +776,11 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx) /* multi-slice MB number or bit size */ ctx->slice_mode = p->slice_mode; reg = 0; - if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) { + if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB) { reg |= (0x1 << 3); writel(reg, mfc_regs->e_enc_options); ctx->slice_size.mb = p->slice_mb; - } else if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) { + } else if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES) { reg |= (0x1 << 3); writel(reg, mfc_regs->e_enc_options); ctx->slice_size.bits = p->slice_bit; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c index 2e62f8721fa5..7d52431c2c83 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c @@ -34,6 +34,11 @@ int s5p_mfc_init_pm(struct s5p_mfc_dev *dev) for (i = 0; i < pm->num_clocks; i++) { pm->clocks[i] = devm_clk_get(pm->device, pm->clk_names[i]); if (IS_ERR(pm->clocks[i])) { + /* additional clocks are optional */ + if (i && PTR_ERR(pm->clocks[i]) == -ENOENT) { + pm->clocks[i] = NULL; + continue; + } mfc_err("Failed to get clock: %s\n", pm->clk_names[i]); return PTR_ERR(pm->clocks[i]); diff --git a/drivers/media/platform/seco-cec/seco-cec.c b/drivers/media/platform/seco-cec/seco-cec.c index e5080d6f5b2d..1d0133f01e00 100644 --- a/drivers/media/platform/seco-cec/seco-cec.c +++ b/drivers/media/platform/seco-cec/seco-cec.c @@ -18,7 +18,7 @@ #include <linux/platform_device.h> /* CEC Framework */ -#include <media/cec.h> +#include <media/cec-notifier.h> #include "seco-cec.h" diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c index 075d4695ee4d..a79250a7f812 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c @@ -143,7 +143,7 @@ int c8sectpfe_frontend_attach(struct dvb_frontend **fe, "%s: stv0367ter_attach failed for NIM card %s\n" , __func__, dvb_card_str(tsin->dvb_card)); return -ENODEV; - }; + } /* * init the demod so that i2c gate_ctrl @@ -203,7 +203,7 @@ int c8sectpfe_frontend_attach(struct dvb_frontend **fe, "%s: stv6110x_attach failed for NIM card %s\n" , __func__, dvb_card_str(tsin->dvb_card)); return -ENODEV; - }; + } stv090x_config.tuner_init = fe2->tuner_init; stv090x_config.tuner_set_mode = fe2->tuner_set_mode; diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/sti/hva/hva-v4l2.c index c42623dccfd6..64004d15a9c9 100644 --- a/drivers/media/platform/sti/hva/hva-v4l2.c +++ b/drivers/media/platform/sti/hva/hva-v4l2.c @@ -566,6 +566,7 @@ static int hva_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) */ struct vb2_queue *vq; struct hva_stream *stream; + struct vb2_buffer *vb2_buf; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, buf->type); @@ -575,7 +576,8 @@ static int hva_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) return -EINVAL; } - stream = (struct hva_stream *)vq->bufs[buf->index]; + vb2_buf = vb2_get_buffer(vq, buf->index); + stream = to_hva_stream(to_vb2_v4l2_buffer(vb2_buf)); stream->bytesused = buf->bytesused; } diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index b9dad0accd1b..d855e9c09c08 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -1702,7 +1702,7 @@ static int dcmi_probe(struct platform_device *pdev) if (irq <= 0) { if (irq != -EPROBE_DEFER) dev_err(&pdev->dev, "Could not get irq\n"); - return irq; + return irq ? irq : -ENXIO; } dcmi->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c index 4c79eb64a7a7..6e0e894154f4 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c @@ -924,6 +924,7 @@ static int sun6i_csi_remove(struct platform_device *pdev) static const struct of_device_id sun6i_csi_of_match[] = { { .compatible = "allwinner,sun6i-a31-csi", }, + { .compatible = "allwinner,sun8i-a83t-csi", }, { .compatible = "allwinner,sun8i-h3-csi", }, { .compatible = "allwinner,sun8i-v3s-csi", }, { .compatible = "allwinner,sun50i-a64-csi", }, diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 4867d0ee803a..dda04498ac56 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -1492,8 +1492,6 @@ static int vpe_querycap(struct file *file, void *priv, strscpy(cap->card, VPE_MODULE_NAME, sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", VPE_MODULE_NAME); - cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1970,12 +1968,12 @@ static const struct v4l2_ctrl_ops vpe_ctrl_ops = { static const struct v4l2_ioctl_ops vpe_ioctl_ops = { .vidioc_querycap = vpe_querycap, - .vidioc_enum_fmt_vid_cap_mplane = vpe_enum_fmt, + .vidioc_enum_fmt_vid_cap = vpe_enum_fmt, .vidioc_g_fmt_vid_cap_mplane = vpe_g_fmt, .vidioc_try_fmt_vid_cap_mplane = vpe_try_fmt, .vidioc_s_fmt_vid_cap_mplane = vpe_s_fmt, - .vidioc_enum_fmt_vid_out_mplane = vpe_enum_fmt, + .vidioc_enum_fmt_vid_out = vpe_enum_fmt, .vidioc_g_fmt_vid_out_mplane = vpe_g_fmt, .vidioc_try_fmt_vid_out_mplane = vpe_try_fmt, .vidioc_s_fmt_vid_out_mplane = vpe_s_fmt, @@ -2408,6 +2406,7 @@ static const struct video_device vpe_videodev = { .minor = -1, .release = video_device_release_empty, .vfl_dir = VFL_DIR_M2M, + .device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING, }; static const struct v4l2_m2m_ops m2m_ops = { diff --git a/drivers/media/platform/vicodec/Kconfig b/drivers/media/platform/vicodec/Kconfig index 36bb0e934252..89456665cb16 100644 --- a/drivers/media/platform/vicodec/Kconfig +++ b/drivers/media/platform/vicodec/Kconfig @@ -4,7 +4,6 @@ config VIDEO_VICODEC depends on VIDEO_DEV && VIDEO_V4L2 select VIDEOBUF2_VMALLOC select V4L2_MEM2MEM_DEV - default n help Driver for a Virtual Codec diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index bd01a9206aa6..7e7c1e80f29f 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -84,6 +84,7 @@ struct vicodec_q_data { unsigned int visible_width; unsigned int visible_height; unsigned int sizeimage; + unsigned int vb2_sizeimage; unsigned int sequence; const struct v4l2_fwht_pixfmt_info *info; }; @@ -116,12 +117,14 @@ struct vicodec_ctx { struct vicodec_dev *dev; bool is_enc; bool is_stateless; + bool is_draining; + bool next_is_last; + bool has_stopped; spinlock_t *lock; struct v4l2_ctrl_handler hdl; struct vb2_v4l2_buffer *last_src_buf; - struct vb2_v4l2_buffer *last_dst_buf; /* Source and destination queue data */ struct vicodec_q_data q_data[2]; @@ -138,6 +141,10 @@ struct vicodec_ctx { bool source_changed; }; +static const struct v4l2_event vicodec_eos_event = { + .type = V4L2_EVENT_EOS +}; + static inline struct vicodec_ctx *file2ctx(struct file *file) { return container_of(file->private_data, struct vicodec_ctx, fh); @@ -329,6 +336,10 @@ static int device_process(struct vicodec_ctx *ctx, copy_cap_to_ref(p_dst, ctx->state.info, &ctx->state); vb2_set_plane_payload(&dst_vb->vb2_buf, 0, q_dst->sizeimage); + if (ntohl(ctx->state.header.flags) & FWHT_FL_I_FRAME) + dst_vb->flags |= V4L2_BUF_FLAG_KEYFRAME; + else + dst_vb->flags |= V4L2_BUF_FLAG_PFRAME; } return ret; } @@ -397,9 +408,6 @@ static enum vb2_buffer_state get_next_header(struct vicodec_ctx *ctx, /* device_run() - prepares and starts the device */ static void device_run(void *priv) { - static const struct v4l2_event eos_event = { - .type = V4L2_EVENT_EOS - }; struct vicodec_ctx *ctx = priv; struct vicodec_dev *dev = ctx->dev; struct vb2_v4l2_buffer *src_buf, *dst_buf; @@ -407,7 +415,6 @@ static void device_run(void *priv) u32 state; struct media_request *src_req; - src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); src_req = src_buf->vb2_buf.req_obj.req; @@ -421,14 +428,14 @@ static void device_run(void *priv) else dst_buf->sequence = q_dst->sequence++; dst_buf->flags &= ~V4L2_BUF_FLAG_LAST; - v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, !ctx->is_enc); - - ctx->last_dst_buf = dst_buf; + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); spin_lock(ctx->lock); if (!ctx->comp_has_next_frame && src_buf == ctx->last_src_buf) { dst_buf->flags |= V4L2_BUF_FLAG_LAST; - v4l2_event_queue_fh(&ctx->fh, &eos_event); + v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); + ctx->is_draining = false; + ctx->has_stopped = true; } if (ctx->is_enc || ctx->is_stateless) { src_buf->sequence = q_src->sequence++; @@ -442,14 +449,14 @@ static void device_run(void *priv) ctx->comp_has_next_frame = false; } v4l2_m2m_buf_done(dst_buf, state); - if (ctx->is_stateless && src_req) - v4l2_ctrl_request_complete(src_req, &ctx->hdl); ctx->comp_size = 0; ctx->header_size = 0; ctx->comp_magic_cnt = 0; ctx->comp_has_frame = false; spin_unlock(ctx->lock); + if (ctx->is_stateless && src_req) + v4l2_ctrl_request_complete(src_req, &ctx->hdl); if (ctx->is_enc) v4l2_m2m_job_finish(dev->stateful_enc.m2m_dev, ctx->fh.m2m_ctx); @@ -579,6 +586,8 @@ static int job_ready(void *priv) unsigned int max_to_copy; unsigned int comp_frame_size; + if (ctx->has_stopped) + return 0; if (ctx->source_changed) return 0; if (ctx->is_stateless || ctx->is_enc || ctx->comp_has_frame) @@ -598,6 +607,8 @@ restart: if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) { state = get_next_header(ctx, &p, p_src + sz - p); if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) { + if (ctx->is_draining && src_buf == ctx->last_src_buf) + return 1; job_remove_src_buf(ctx, state); goto restart; } @@ -625,6 +636,8 @@ restart: p += copy; ctx->comp_size += copy; if (ctx->comp_size < max_to_copy) { + if (ctx->is_draining && src_buf == ctx->last_src_buf) + return 1; job_remove_src_buf(ctx, state); goto restart; } @@ -666,7 +679,6 @@ restart: v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); update_capture_data_from_header(ctx); - ctx->first_source_change_sent = true; v4l2_event_queue_fh(&ctx->fh, &rs_event); set_last_buffer(dst_buf, src_buf, ctx); ctx->source_changed = true; @@ -713,7 +725,8 @@ static int enum_fmt(struct v4l2_fmtdesc *f, struct vicodec_ctx *ctx, const struct v4l2_fwht_pixfmt_info *info = get_q_data(ctx, f->type)->info; - if (!info || ctx->is_enc) + if (ctx->is_enc || + !vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q)) info = v4l2_fwht_get_pixfmt(f->index); else info = v4l2_fwht_find_nth_fmt(info->width_div, @@ -764,9 +777,6 @@ static int vidioc_g_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) q_data = get_q_data(ctx, f->type); info = q_data->info; - if (!info) - info = v4l2_fwht_get_pixfmt(0); - switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: @@ -1032,16 +1042,10 @@ static int vidioc_s_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) default: return -EINVAL; } - if (q_data->visible_width > q_data->coded_width) - q_data->visible_width = q_data->coded_width; - if (q_data->visible_height > q_data->coded_height) - q_data->visible_height = q_data->coded_height; - dprintk(ctx->dev, - "Setting format for type %d, coded wxh: %dx%d, visible wxh: %dx%d, fourcc: %08x\n", + "Setting format for type %d, coded wxh: %dx%d, fourcc: 0x%08x\n", f->type, q_data->coded_width, q_data->coded_height, - q_data->visible_width, q_data->visible_height, q_data->info->id); return 0; @@ -1063,18 +1067,58 @@ static int vidioc_s_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f) { struct vicodec_ctx *ctx = file2ctx(file); - struct v4l2_pix_format_mplane *pix_mp; + struct vicodec_q_data *q_data; + struct vicodec_q_data *q_data_cap; struct v4l2_pix_format *pix; + struct v4l2_pix_format_mplane *pix_mp; + u32 coded_w = 0, coded_h = 0; + unsigned int size = 0; int ret; + q_data = get_q_data(ctx, f->type); + q_data_cap = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + ret = vidioc_try_fmt_vid_out(file, priv, f); if (ret) return ret; + if (ctx->is_enc) { + struct vb2_queue *vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + struct vb2_queue *vq_cap = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + const struct v4l2_fwht_pixfmt_info *info = ctx->is_stateless ? + &pixfmt_stateless_fwht : &pixfmt_fwht; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + coded_w = f->fmt.pix.width; + coded_h = f->fmt.pix.height; + } else { + coded_w = f->fmt.pix_mp.width; + coded_h = f->fmt.pix_mp.height; + } + if (vb2_is_busy(vq) && (coded_w != q_data->coded_width || + coded_h != q_data->coded_height)) + return -EBUSY; + size = coded_w * coded_h * + info->sizeimage_mult / info->sizeimage_div; + if (!ctx->is_stateless) + size += sizeof(struct fwht_cframe_hdr); + + if (vb2_is_busy(vq_cap) && size > q_data_cap->sizeimage) + return -EBUSY; + } + ret = vidioc_s_fmt(file2ctx(file), f); if (!ret) { + if (ctx->is_enc) { + q_data->visible_width = coded_w; + q_data->visible_height = coded_h; + q_data_cap->coded_width = coded_w; + q_data_cap->coded_height = coded_h; + q_data_cap->sizeimage = size; + } + switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: pix = &f->fmt.pix; ctx->state.colorspace = pix->colorspace; @@ -1082,7 +1126,6 @@ static int vidioc_s_fmt_vid_out(struct file *file, void *priv, ctx->state.ycbcr_enc = pix->ycbcr_enc; ctx->state.quantization = pix->quantization; break; - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: pix_mp = &f->fmt.pix_mp; ctx->state.colorspace = pix_mp->colorspace; @@ -1173,31 +1216,39 @@ static int vidioc_s_selection(struct file *file, void *priv, return 0; } -static void vicodec_mark_last_buf(struct vicodec_ctx *ctx) +static int vicodec_mark_last_buf(struct vicodec_ctx *ctx) { - static const struct v4l2_event eos_event = { - .type = V4L2_EVENT_EOS - }; + struct vb2_v4l2_buffer *next_dst_buf; + int ret = 0; spin_lock(ctx->lock); - ctx->last_src_buf = v4l2_m2m_last_src_buf(ctx->fh.m2m_ctx); - if (!ctx->last_src_buf && ctx->last_dst_buf) { - ctx->last_dst_buf->flags |= V4L2_BUF_FLAG_LAST; - v4l2_event_queue_fh(&ctx->fh, &eos_event); + if (ctx->is_draining) { + ret = -EBUSY; + goto unlock; } - spin_unlock(ctx->lock); -} + if (ctx->has_stopped) + goto unlock; -static int vicodec_try_encoder_cmd(struct file *file, void *fh, - struct v4l2_encoder_cmd *ec) -{ - if (ec->cmd != V4L2_ENC_CMD_STOP) - return -EINVAL; + ctx->last_src_buf = v4l2_m2m_last_src_buf(ctx->fh.m2m_ctx); + ctx->is_draining = true; + if (ctx->last_src_buf) + goto unlock; + + next_dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + if (!next_dst_buf) { + ctx->next_is_last = true; + goto unlock; + } - if (ec->flags & V4L2_ENC_CMD_STOP_AT_GOP_END) - return -EINVAL; + next_dst_buf->flags |= V4L2_BUF_FLAG_LAST; + vb2_buffer_done(&next_dst_buf->vb2_buf, VB2_BUF_STATE_DONE); + ctx->is_draining = false; + ctx->has_stopped = true; + v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); - return 0; +unlock: + spin_unlock(ctx->lock); + return ret; } static int vicodec_encoder_cmd(struct file *file, void *fh, @@ -1206,27 +1257,26 @@ static int vicodec_encoder_cmd(struct file *file, void *fh, struct vicodec_ctx *ctx = file2ctx(file); int ret; - ret = vicodec_try_encoder_cmd(file, fh, ec); + ret = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, ec); if (ret < 0) return ret; - vicodec_mark_last_buf(ctx); - return 0; -} - -static int vicodec_try_decoder_cmd(struct file *file, void *fh, - struct v4l2_decoder_cmd *dc) -{ - if (dc->cmd != V4L2_DEC_CMD_STOP) - return -EINVAL; - - if (dc->flags & V4L2_DEC_CMD_STOP_TO_BLACK) - return -EINVAL; - - if (!(dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) && (dc->stop.pts != 0)) - return -EINVAL; + if (!vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q) || + !vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q)) + return 0; - return 0; + if (ec->cmd == V4L2_ENC_CMD_STOP) + return vicodec_mark_last_buf(ctx); + ret = 0; + spin_lock(ctx->lock); + if (ctx->is_draining) { + ret = -EBUSY; + } else if (ctx->has_stopped) { + ctx->has_stopped = false; + vb2_clear_last_buffer_dequeued(&ctx->fh.m2m_ctx->cap_q_ctx.q); + } + spin_unlock(ctx->lock); + return ret; } static int vicodec_decoder_cmd(struct file *file, void *fh, @@ -1235,12 +1285,26 @@ static int vicodec_decoder_cmd(struct file *file, void *fh, struct vicodec_ctx *ctx = file2ctx(file); int ret; - ret = vicodec_try_decoder_cmd(file, fh, dc); + ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, dc); if (ret < 0) return ret; - vicodec_mark_last_buf(ctx); - return 0; + if (!vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q) || + !vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q)) + return 0; + + if (dc->cmd == V4L2_DEC_CMD_STOP) + return vicodec_mark_last_buf(ctx); + ret = 0; + spin_lock(ctx->lock); + if (ctx->is_draining) { + ret = -EBUSY; + } else if (ctx->has_stopped) { + ctx->has_stopped = false; + vb2_clear_last_buffer_dequeued(&ctx->fh.m2m_ctx->cap_q_ctx.q); + } + spin_unlock(ctx->lock); + return ret; } static int vicodec_enum_framesizes(struct file *file, void *fh, @@ -1283,6 +1347,8 @@ static int vicodec_subscribe_event(struct v4l2_fh *fh, return -EINVAL; /* fall through */ case V4L2_EVENT_EOS: + if (ctx->is_stateless) + return -EINVAL; return v4l2_event_subscribe(fh, sub, 0, NULL); default: return v4l2_ctrl_subscribe_event(fh, sub); @@ -1297,7 +1363,6 @@ static const struct v4l2_ioctl_ops vicodec_ioctl_ops = { .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap, @@ -1307,7 +1372,6 @@ static const struct v4l2_ioctl_ops vicodec_ioctl_ops = { .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, - .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out, .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt_vid_out, .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out, .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt_vid_out, @@ -1326,9 +1390,9 @@ static const struct v4l2_ioctl_ops vicodec_ioctl_ops = { .vidioc_g_selection = vidioc_g_selection, .vidioc_s_selection = vidioc_s_selection, - .vidioc_try_encoder_cmd = vicodec_try_encoder_cmd, + .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, .vidioc_encoder_cmd = vicodec_encoder_cmd, - .vidioc_try_decoder_cmd = vicodec_try_decoder_cmd, + .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd, .vidioc_decoder_cmd = vicodec_decoder_cmd, .vidioc_enum_framesizes = vicodec_enum_framesizes, @@ -1354,6 +1418,7 @@ static int vicodec_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, *nplanes = 1; sizes[0] = size; + q_data->vb2_sizeimage = size; return 0; } @@ -1384,11 +1449,11 @@ static int vicodec_buf_prepare(struct vb2_buffer *vb) } } - if (vb2_plane_size(vb, 0) < q_data->sizeimage) { + if (vb2_plane_size(vb, 0) < q_data->vb2_sizeimage) { dprintk(ctx->dev, "%s data will not fit into plane (%lu < %lu)\n", __func__, vb2_plane_size(vb, 0), - (long)q_data->sizeimage); + (long)q_data->vb2_sizeimage); return -EINVAL; } @@ -1412,6 +1477,25 @@ static void vicodec_buf_queue(struct vb2_buffer *vb) .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, }; + if (vb2_is_streaming(vq_cap)) { + if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type) && + ctx->next_is_last) { + unsigned int i; + + for (i = 0; i < vb->num_planes; i++) + vb->planes[i].bytesused = 0; + vbuf->flags = V4L2_BUF_FLAG_LAST; + vbuf->field = V4L2_FIELD_NONE; + vbuf->sequence = get_q_data(ctx, vb->vb2_queue->type)->sequence++; + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + ctx->is_draining = false; + ctx->has_stopped = true; + ctx->next_is_last = false; + v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); + return; + } + } + /* buf_queue handles only the first source change event */ if (ctx->first_source_change_sent) { v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); @@ -1519,16 +1603,11 @@ static int vicodec_start_streaming(struct vb2_queue *q, unsigned int total_planes_size; u8 *new_comp_frame = NULL; - if (!info) - return -EINVAL; - chroma_div = info->width_div * info->height_div; q_data->sequence = 0; if (V4L2_TYPE_IS_OUTPUT(q->type)) ctx->last_src_buf = NULL; - else - ctx->last_dst_buf = NULL; state->gop_cnt = 0; @@ -1604,6 +1683,32 @@ static void vicodec_stop_streaming(struct vb2_queue *q) vicodec_return_bufs(q, VB2_BUF_STATE_ERROR); + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + if (ctx->is_draining) { + struct vb2_v4l2_buffer *next_dst_buf; + + spin_lock(ctx->lock); + ctx->last_src_buf = NULL; + next_dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + if (!next_dst_buf) { + ctx->next_is_last = true; + } else { + next_dst_buf->flags |= V4L2_BUF_FLAG_LAST; + vb2_buffer_done(&next_dst_buf->vb2_buf, VB2_BUF_STATE_DONE); + ctx->is_draining = false; + ctx->has_stopped = true; + v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); + } + spin_unlock(ctx->lock); + } + } else { + ctx->is_draining = false; + ctx->has_stopped = false; + ctx->next_is_last = false; + } + if (!ctx->is_enc && V4L2_TYPE_IS_OUTPUT(q->type)) + ctx->first_source_change_sent = false; + if ((!V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) || (V4L2_TYPE_IS_OUTPUT(q->type) && ctx->is_enc)) { if (!ctx->is_stateless) @@ -1771,11 +1876,13 @@ static const struct v4l2_ctrl_config vicodec_ctrl_stateless_state = { */ static int vicodec_open(struct file *file) { + const struct v4l2_fwht_pixfmt_info *info = v4l2_fwht_get_pixfmt(0); struct video_device *vfd = video_devdata(file); struct vicodec_dev *dev = video_drvdata(file); struct vicodec_ctx *ctx = NULL; struct v4l2_ctrl_handler *hdl; - unsigned int size; + unsigned int raw_size; + unsigned int comp_size; int rc = 0; if (mutex_lock_interruptible(vfd->lock)) @@ -1795,13 +1902,16 @@ static int vicodec_open(struct file *file) file->private_data = &ctx->fh; ctx->dev = dev; hdl = &ctx->hdl; - v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_handler_init(hdl, 5); v4l2_ctrl_new_std(hdl, &vicodec_ctrl_ops, V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 16, 1, 10); v4l2_ctrl_new_std(hdl, &vicodec_ctrl_ops, V4L2_CID_FWHT_I_FRAME_QP, 1, 31, 1, 20); v4l2_ctrl_new_std(hdl, &vicodec_ctrl_ops, V4L2_CID_FWHT_P_FRAME_QP, 1, 31, 1, 20); + if (ctx->is_enc) + v4l2_ctrl_new_std(hdl, &vicodec_ctrl_ops, + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 1, 1, 1, 1); if (ctx->is_stateless) v4l2_ctrl_new_custom(hdl, &vicodec_ctrl_stateless_state, NULL); if (hdl->error) { @@ -1814,7 +1924,7 @@ static int vicodec_open(struct file *file) v4l2_ctrl_handler_setup(hdl); if (ctx->is_enc) - ctx->q_data[V4L2_M2M_SRC].info = v4l2_fwht_get_pixfmt(0); + ctx->q_data[V4L2_M2M_SRC].info = info; else if (ctx->is_stateless) ctx->q_data[V4L2_M2M_SRC].info = &pixfmt_stateless_fwht; else @@ -1823,22 +1933,24 @@ static int vicodec_open(struct file *file) ctx->q_data[V4L2_M2M_SRC].coded_height = 720; ctx->q_data[V4L2_M2M_SRC].visible_width = 1280; ctx->q_data[V4L2_M2M_SRC].visible_height = 720; - size = 1280 * 720 * ctx->q_data[V4L2_M2M_SRC].info->sizeimage_mult / - ctx->q_data[V4L2_M2M_SRC].info->sizeimage_div; - if (ctx->is_enc || ctx->is_stateless) - ctx->q_data[V4L2_M2M_SRC].sizeimage = size; + raw_size = 1280 * 720 * info->sizeimage_mult / info->sizeimage_div; + comp_size = 1280 * 720 * pixfmt_fwht.sizeimage_mult / + pixfmt_fwht.sizeimage_div; + if (ctx->is_enc) + ctx->q_data[V4L2_M2M_SRC].sizeimage = raw_size; + else if (ctx->is_stateless) + ctx->q_data[V4L2_M2M_SRC].sizeimage = comp_size; else ctx->q_data[V4L2_M2M_SRC].sizeimage = - size + sizeof(struct fwht_cframe_hdr); + comp_size + sizeof(struct fwht_cframe_hdr); + ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC]; if (ctx->is_enc) { - ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC]; ctx->q_data[V4L2_M2M_DST].info = &pixfmt_fwht; - ctx->q_data[V4L2_M2M_DST].sizeimage = 1280 * 720 * - ctx->q_data[V4L2_M2M_DST].info->sizeimage_mult / - ctx->q_data[V4L2_M2M_DST].info->sizeimage_div + - sizeof(struct fwht_cframe_hdr); + ctx->q_data[V4L2_M2M_DST].sizeimage = + comp_size + sizeof(struct fwht_cframe_hdr); } else { - ctx->q_data[V4L2_M2M_DST].info = NULL; + ctx->q_data[V4L2_M2M_DST].info = info; + ctx->q_data[V4L2_M2M_DST].sizeimage = raw_size; } ctx->state.colorspace = V4L2_COLORSPACE_REC709; @@ -2013,18 +2125,31 @@ static int register_instance(struct vicodec_dev *dev, return 0; } +static void vicodec_v4l2_dev_release(struct v4l2_device *v4l2_dev) +{ + struct vicodec_dev *dev = container_of(v4l2_dev, struct vicodec_dev, v4l2_dev); + + v4l2_device_unregister(&dev->v4l2_dev); + v4l2_m2m_release(dev->stateful_enc.m2m_dev); + v4l2_m2m_release(dev->stateful_dec.m2m_dev); + v4l2_m2m_release(dev->stateless_dec.m2m_dev); + kfree(dev); +} + static int vicodec_probe(struct platform_device *pdev) { struct vicodec_dev *dev; int ret; - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) - return ret; + goto free_dev; + + dev->v4l2_dev.release = vicodec_v4l2_dev_release; #ifdef CONFIG_MEDIA_CONTROLLER dev->mdev.dev = &pdev->dev; @@ -2102,6 +2227,8 @@ unreg_sf_enc: v4l2_m2m_release(dev->stateful_enc.m2m_dev); unreg_dev: v4l2_device_unregister(&dev->v4l2_dev); +free_dev: + kfree(dev); return ret; } @@ -2120,12 +2247,10 @@ static int vicodec_remove(struct platform_device *pdev) media_device_cleanup(&dev->mdev); #endif - v4l2_m2m_release(dev->stateful_enc.m2m_dev); - v4l2_m2m_release(dev->stateful_dec.m2m_dev); video_unregister_device(&dev->stateful_enc.vfd); video_unregister_device(&dev->stateful_dec.vfd); video_unregister_device(&dev->stateless_dec.vfd); - v4l2_device_unregister(&dev->v4l2_dev); + v4l2_device_put(&dev->v4l2_dev); return 0; } diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index 243c82b5d537..acd3bd48c7e2 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -1359,7 +1359,7 @@ static int vim2m_probe(struct platform_device *pdev) MEDIA_ENT_F_PROC_VIDEO_SCALER); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller\n"); - goto error_m2m; + goto error_dev; } ret = media_device_register(&dev->mdev); @@ -1373,11 +1373,11 @@ static int vim2m_probe(struct platform_device *pdev) #ifdef CONFIG_MEDIA_CONTROLLER error_m2m_mc: v4l2_m2m_unregister_media_controller(dev->m2m_dev); -error_m2m: - v4l2_m2m_release(dev->m2m_dev); #endif error_dev: video_unregister_device(&dev->vfd); + /* vim2m_device_release called by video_unregister_device to release various objects */ + return ret; error_v4l2: v4l2_device_unregister(&dev->v4l2_dev); error_free: diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig index beba6acce593..bd221d3e1a4a 100644 --- a/drivers/media/platform/vimc/Kconfig +++ b/drivers/media/platform/vimc/Kconfig @@ -4,7 +4,6 @@ config VIDEO_VIMC depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API select VIDEOBUF2_VMALLOC select VIDEO_V4L2_TPG - default n help Skeleton driver for Virtual Media Controller diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile index c4fc8e7d365a..96d06f030c31 100644 --- a/drivers/media/platform/vimc/Makefile +++ b/drivers/media/platform/vimc/Makefile @@ -1,11 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -vimc-objs := vimc-core.o -vimc_capture-objs := vimc-capture.o -vimc_common-objs := vimc-common.o -vimc_debayer-objs := vimc-debayer.o -vimc_scaler-objs := vimc-scaler.o -vimc_sensor-objs := vimc-sensor.o -vimc_streamer-objs := vimc-streamer.o +vimc-y := vimc-core.o vimc-common.o vimc-streamer.o -obj-$(CONFIG_VIDEO_VIMC) += vimc.o vimc_capture.o vimc_common.o vimc-debayer.o \ - vimc_scaler.o vimc_sensor.o vimc_streamer.o +obj-$(CONFIG_VIDEO_VIMC) += vimc.o vimc-capture.o vimc-debayer.o \ + vimc-scaler.o vimc-sensor.o diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c index 946dc0908566..664855708fdf 100644 --- a/drivers/media/platform/vimc/vimc-capture.c +++ b/drivers/media/platform/vimc/vimc-capture.c @@ -142,12 +142,15 @@ static int vimc_cap_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct vimc_cap_device *vcap = video_drvdata(file); + int ret; /* Do not change the format while stream is on */ if (vb2_is_busy(&vcap->queue)) return -EBUSY; - vimc_cap_try_fmt_vid_cap(file, priv, f); + ret = vimc_cap_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; dev_dbg(vcap->dev, "%s: format update: " "old:%dx%d (0x%x, %d, %d, %d, %d) " diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c index f4d2073076ed..03016f204d05 100644 --- a/drivers/media/platform/vimc/vimc-common.c +++ b/drivers/media/platform/vimc/vimc-common.c @@ -377,7 +377,3 @@ void vimc_ent_sd_unregister(struct vimc_ent_device *ved, struct v4l2_subdev *sd) v4l2_device_unregister_subdev(sd); } EXPORT_SYMBOL_GPL(vimc_ent_sd_unregister); - -MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Common"); -MODULE_AUTHOR("Helen Koike <helen.fornazier@gmail.com>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c index 03707bdcbfa8..571c55aa0e16 100644 --- a/drivers/media/platform/vimc/vimc-core.c +++ b/drivers/media/platform/vimc/vimc-core.c @@ -234,10 +234,7 @@ static void vimc_comp_unbind(struct device *master) static int vimc_comp_compare(struct device *comp, void *data) { - const struct platform_device *pdev = to_platform_device(comp); - const char *name = data; - - return !strcmp(pdev->dev.platform_data, name); + return comp == data; } static struct component_match *vimc_add_subdevs(struct vimc_device *vimc) @@ -267,7 +264,7 @@ static struct component_match *vimc_add_subdevs(struct vimc_device *vimc) } component_match_add(&vimc->pdev.dev, &match, vimc_comp_compare, - (void *)vimc->pipe_cfg->ents[i].name); + &vimc->subdevs[i]->dev); } return match; diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c index 3ae8c12f5fa3..00598fbf3cba 100644 --- a/drivers/media/platform/vimc/vimc-debayer.c +++ b/drivers/media/platform/vimc/vimc-debayer.c @@ -16,14 +16,16 @@ #include "vimc-common.h" #define VIMC_DEB_DRV_NAME "vimc-debayer" -/* This module only supports tranforming a bayer format to V4L2_PIX_FMT_RGB24 */ +/* This module only supports transforming a bayer format + * to V4L2_PIX_FMT_RGB24 + */ #define VIMC_DEB_SRC_PIXFMT V4L2_PIX_FMT_RGB24 #define VIMC_DEB_SRC_MBUS_FMT_DEFAULT MEDIA_BUS_FMT_RGB888_1X24 static unsigned int deb_mean_win_size = 3; module_param(deb_mean_win_size, uint, 0000); MODULE_PARM_DESC(deb_mean_win_size, " the window size to calculate the mean.\n" - "NOTE: the window size need to be an odd number, as the main pixel " + "NOTE: the window size needs to be an odd number, as the main pixel " "stays in the center of the window, otherwise the next odd number " "is considered"); @@ -260,7 +262,7 @@ static int vimc_deb_set_fmt(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { /* Do not change the format while stream is on */ - if (vdeb->src_frame) + if (vdeb->ved.stream) return -EBUSY; sink_fmt = &vdeb->sink_fmt; @@ -327,9 +329,6 @@ static int vimc_deb_s_stream(struct v4l2_subdev *sd, int enable) const struct v4l2_format_info *pix_info; unsigned int frame_size; - if (vdeb->src_frame) - return 0; - /* We only support translating bayer to RGB24 */ if (src_pixelformat != V4L2_PIX_FMT_RGB24) { dev_err(vdeb->dev, diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c index 5f31c1e351a3..c7123a45c55b 100644 --- a/drivers/media/platform/vimc/vimc-scaler.c +++ b/drivers/media/platform/vimc/vimc-scaler.c @@ -148,7 +148,7 @@ static int vimc_sca_set_fmt(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { /* Do not change the format while stream is on */ - if (vsca->src_frame) + if (vsca->ved.stream) return -EBUSY; sink_fmt = &vsca->sink_fmt; @@ -203,9 +203,6 @@ static int vimc_sca_s_stream(struct v4l2_subdev *sd, int enable) const struct v4l2_format_info *pix_info; unsigned int frame_size; - if (vsca->src_frame) - return 0; - if (!vimc_sca_is_pixfmt_supported(pixelformat)) { dev_err(vsca->dev, "pixfmt (0x%08x) is not supported\n", pixelformat); @@ -327,7 +324,7 @@ static void *vimc_sca_process_frame(struct vimc_ent_device *ved, ved); /* If the stream in this node is not active, just return */ - if (!vsca->src_frame) + if (!ved->stream) return ERR_PTR(-EINVAL); vimc_sca_fill_src_frame(vsca, sink_frame); diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c index 46a25f705456..51359472eef2 100644 --- a/drivers/media/platform/vimc/vimc-sensor.c +++ b/drivers/media/platform/vimc/vimc-sensor.c @@ -131,7 +131,7 @@ static int vimc_sen_set_fmt(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { /* Do not change the format while stream is on */ - if (vsen->frame) + if (vsen->ved.stream) return -EBUSY; mf = &vsen->mbus_format; @@ -187,10 +187,6 @@ static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable) const struct v4l2_format_info *pix_info; unsigned int frame_size; - if (vsen->kthread_sen) - /* tpg is already executing */ - return 0; - /* Calculate the frame size */ pix_info = v4l2_format_info(pixelformat); frame_size = vsen->mbus_format.width * pix_info->bpp[0] * @@ -211,7 +207,6 @@ static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable) vfree(vsen->frame); vsen->frame = NULL; - return 0; } return 0; diff --git a/drivers/media/platform/vimc/vimc-streamer.c b/drivers/media/platform/vimc/vimc-streamer.c index 26b674259489..3b3f36357a0e 100644 --- a/drivers/media/platform/vimc/vimc-streamer.c +++ b/drivers/media/platform/vimc/vimc-streamer.c @@ -122,6 +122,14 @@ static int vimc_streamer_pipeline_init(struct vimc_stream *stream, return -EINVAL; } +/* + * vimc_streamer_thread - process frames through the pipeline + * + * @data: vimc_stream struct of the current stream + * + * From the source to the sink, gets a frame from each subdevice and send to + * the next one of the pipeline at a fixed framerate. + */ static int vimc_streamer_thread(void *data) { struct vimc_stream *stream = data; @@ -149,6 +157,20 @@ static int vimc_streamer_thread(void *data) return 0; } +/* + * vimc_streamer_s_stream - start/stop the streaming on the media pipeline + * + * @stream: the pointer to the stream structure of the current stream + * @ved: pointer to the vimc entity of the entity of the stream + * @enable: flag to determine if stream should start/stop + * + * When starting, check if there is no stream->kthread allocated. This should + * indicate that a stream is already running. Then, it initializes + * the pipeline, creates and runs a kthread to consume buffers through the + * pipeline. + * When stopping, analogously check if there is a stream running, stop + * the thread and terminates the pipeline. + */ int vimc_streamer_s_stream(struct vimc_stream *stream, struct vimc_ent_device *ved, int enable) @@ -188,7 +210,3 @@ int vimc_streamer_s_stream(struct vimc_stream *stream, return 0; } EXPORT_SYMBOL_GPL(vimc_streamer_s_stream); - -MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Streamer"); -MODULE_AUTHOR("Lucas A. M. Magalhães <lucmaga@gmail.com>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/vivid/Kconfig b/drivers/media/platform/vivid/Kconfig index b172bcc11758..e2ff06edfa93 100644 --- a/drivers/media/platform/vivid/Kconfig +++ b/drivers/media/platform/vivid/Kconfig @@ -11,7 +11,6 @@ config VIDEO_VIVID select VIDEOBUF2_VMALLOC select VIDEOBUF2_DMA_CONTIG select VIDEO_V4L2_TPG - default n help Enables a virtual video driver. This driver emulates a webcam, TV, S-Video and HDMI capture hardware, including VBI support for diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c index 7047df6f0e0e..bc2a176937a4 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/platform/vivid/vivid-core.c @@ -500,20 +500,18 @@ static const struct v4l2_file_operations vivid_radio_fops = { static const struct v4l2_ioctl_ops vivid_ioctl_ops = { .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid, + .vidioc_enum_fmt_vid_cap = vivid_enum_fmt_vid, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_mplane, .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap_mplane, .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane, .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap_mplane, - .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid, + .vidioc_enum_fmt_vid_out = vivid_enum_fmt_vid, .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out, .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, - .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_mplane, .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt_vid_out_mplane, .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out_mplane, .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt_vid_out_mplane, @@ -669,6 +667,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) v4l2_std_id tvnorms_cap = 0, tvnorms_out = 0; int ret; int i; +#ifdef CONFIG_VIDEO_VIVID_CEC + unsigned int cec_tx_bus_cnt = 0; +#endif /* allocate main vivid state structure */ dev = kzalloc(sizeof(*dev), GFP_KERNEL); @@ -722,6 +723,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) in_type_counter[HDMI]--; dev->num_inputs--; } + dev->num_hdmi_inputs = in_type_counter[HDMI]; /* how many outputs do we have and of what type? */ dev->num_outputs = num_outputs[inst]; @@ -732,6 +734,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) for (i = 0; i < dev->num_outputs; i++) { dev->output_type[i] = ((output_types[inst] >> i) & 1) ? HDMI : SVID; dev->output_name_counter[i] = out_type_counter[dev->output_type[i]]++; + dev->display_present[i] = true; } dev->has_audio_outputs = out_type_counter[SVID]; if (out_type_counter[HDMI] == 16) { @@ -743,6 +746,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) out_type_counter[HDMI]--; dev->num_outputs--; } + dev->num_hdmi_outputs = out_type_counter[HDMI]; /* do we create a video capture device? */ dev->has_vid_cap = node_type & 0x0001; @@ -1001,13 +1005,15 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->webcam_size_idx = 1; dev->webcam_ival_idx = 3; tpg_s_fourcc(&dev->tpg, dev->fmt_cap->fourcc); - dev->std_cap = V4L2_STD_PAL; dev->std_out = V4L2_STD_PAL; if (dev->input_type[0] == TV || dev->input_type[0] == SVID) tvnorms_cap = V4L2_STD_ALL; if (dev->output_type[0] == SVID) tvnorms_out = V4L2_STD_ALL; - dev->dv_timings_cap = def_dv_timings; + for (i = 0; i < MAX_INPUTS; i++) { + dev->dv_timings_cap[i] = def_dv_timings; + dev->std_cap[i] = V4L2_STD_PAL; + } dev->dv_timings_out = def_dv_timings; dev->tv_freq = 2804 /* 175.25 * 16 */; dev->tv_audmode = V4L2_TUNER_MODE_STEREO; @@ -1037,6 +1043,17 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) if (ret) goto unreg_dev; + /* enable/disable interface specific controls */ + if (dev->num_outputs && dev->output_type[0] != HDMI) + v4l2_ctrl_activate(dev->ctrl_display_present, false); + if (dev->num_inputs && dev->input_type[0] != HDMI) { + v4l2_ctrl_activate(dev->ctrl_dv_timings_signal_mode, false); + v4l2_ctrl_activate(dev->ctrl_dv_timings, false); + } else if (dev->num_inputs && dev->input_type[0] == HDMI) { + v4l2_ctrl_activate(dev->ctrl_std_signal_mode, false); + v4l2_ctrl_activate(dev->ctrl_standard, false); + } + /* * update the capture and output formats to do a proper initial * configuration. @@ -1044,14 +1061,6 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) vivid_update_format_cap(dev, false); vivid_update_format_out(dev); - v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vid_cap); - v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vid_out); - v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vbi_cap); - v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vbi_out); - v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_rx); - v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_tx); - v4l2_ctrl_handler_setup(&dev->ctrl_hdl_sdr_cap); - /* initialize overlay */ dev->fb_cap.fmt.width = dev->src_rect.width; dev->fb_cap.fmt.height = dev->src_rect.height; @@ -1212,6 +1221,47 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->fb_info.node); } +#ifdef CONFIG_VIDEO_VIVID_CEC + if (dev->has_vid_cap && in_type_counter[HDMI]) { + struct cec_adapter *adap; + + adap = vivid_cec_alloc_adap(dev, 0, false); + ret = PTR_ERR_OR_ZERO(adap); + if (ret < 0) + goto unreg_dev; + dev->cec_rx_adap = adap; + } + + if (dev->has_vid_out) { + for (i = 0; i < dev->num_outputs; i++) { + struct cec_adapter *adap; + + if (dev->output_type[i] != HDMI) + continue; + + dev->cec_output2bus_map[i] = cec_tx_bus_cnt; + adap = vivid_cec_alloc_adap(dev, cec_tx_bus_cnt, true); + ret = PTR_ERR_OR_ZERO(adap); + if (ret < 0) { + for (i = 0; i < dev->num_outputs; i++) + cec_delete_adapter(dev->cec_tx_adap[i]); + goto unreg_dev; + } + + dev->cec_tx_adap[cec_tx_bus_cnt] = adap; + cec_tx_bus_cnt++; + } + } +#endif + + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vid_cap); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vid_out); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vbi_cap); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vbi_out); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_rx); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_tx); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_sdr_cap); + /* finally start creating the device nodes */ if (dev->has_vid_cap) { vfd = &dev->vid_cap_dev; @@ -1241,22 +1291,15 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) #ifdef CONFIG_VIDEO_VIVID_CEC if (in_type_counter[HDMI]) { - struct cec_adapter *adap; - - adap = vivid_cec_alloc_adap(dev, 0, false); - ret = PTR_ERR_OR_ZERO(adap); - if (ret < 0) - goto unreg_dev; - dev->cec_rx_adap = adap; - ret = cec_register_adapter(adap, &pdev->dev); + ret = cec_register_adapter(dev->cec_rx_adap, &pdev->dev); if (ret < 0) { - cec_delete_adapter(adap); + cec_delete_adapter(dev->cec_rx_adap); dev->cec_rx_adap = NULL; goto unreg_dev; } - cec_s_phys_addr(adap, 0, false); + cec_s_phys_addr(dev->cec_rx_adap, 0, false); v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI input 0\n", - dev_name(&adap->devnode.dev)); + dev_name(&dev->cec_rx_adap->devnode.dev)); } #endif @@ -1268,10 +1311,6 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) } if (dev->has_vid_out) { -#ifdef CONFIG_VIDEO_VIVID_CEC - unsigned int bus_cnt = 0; -#endif - vfd = &dev->vid_out_dev; snprintf(vfd->name, sizeof(vfd->name), "vivid-%03d-vid-out", inst); @@ -1299,30 +1338,21 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) #endif #ifdef CONFIG_VIDEO_VIVID_CEC - for (i = 0; i < dev->num_outputs; i++) { - struct cec_adapter *adap; - - if (dev->output_type[i] != HDMI) - continue; - dev->cec_output2bus_map[i] = bus_cnt; - adap = vivid_cec_alloc_adap(dev, bus_cnt, true); - ret = PTR_ERR_OR_ZERO(adap); - if (ret < 0) - goto unreg_dev; - dev->cec_tx_adap[bus_cnt] = adap; - ret = cec_register_adapter(adap, &pdev->dev); + for (i = 0; i < cec_tx_bus_cnt; i++) { + ret = cec_register_adapter(dev->cec_tx_adap[i], &pdev->dev); if (ret < 0) { - cec_delete_adapter(adap); - dev->cec_tx_adap[bus_cnt] = NULL; + for (; i < cec_tx_bus_cnt; i++) { + cec_delete_adapter(dev->cec_tx_adap[i]); + dev->cec_tx_adap[i] = NULL; + } goto unreg_dev; } v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI output %d\n", - dev_name(&adap->devnode.dev), bus_cnt); - bus_cnt++; - if (bus_cnt <= out_type_counter[HDMI]) - cec_s_phys_addr(adap, bus_cnt << 12, false); + dev_name(&dev->cec_tx_adap[i]->devnode.dev), i); + if (i <= out_type_counter[HDMI]) + cec_s_phys_addr(dev->cec_tx_adap[i], i << 12, false); else - cec_s_phys_addr(adap, 0x1000, false); + cec_s_phys_addr(dev->cec_tx_adap[i], 0x1000, false); } #endif diff --git a/drivers/media/platform/vivid/vivid-core.h b/drivers/media/platform/vivid/vivid-core.h index 6697c7009629..7ebb14673c75 100644 --- a/drivers/media/platform/vivid/vivid-core.h +++ b/drivers/media/platform/vivid/vivid-core.h @@ -22,18 +22,6 @@ #define dprintk(dev, level, fmt, arg...) \ v4l2_dbg(level, vivid_debug, &dev->v4l2_dev, fmt, ## arg) -/* Maximum allowed frame rate - * - * vivid will allow setting timeperframe in [1/FPS_MAX - FPS_MAX/1] range. - * - * Ideally FPS_MAX should be infinity, i.e. practically UINT_MAX, but that - * might hit application errors when they manipulate these values. - * - * Besides, for tpf < 10ms image-generation logic should be changed, to avoid - * producing frames with equal content. - */ -#define FPS_MAX 100 - /* The maximum number of clip rectangles */ #define MAX_CLIPS 16 /* The maximum number of inputs */ @@ -180,9 +168,11 @@ struct vivid_dev { /* supported features */ bool multiplanar; unsigned num_inputs; + unsigned int num_hdmi_inputs; u8 input_type[MAX_INPUTS]; u8 input_name_counter[MAX_INPUTS]; unsigned num_outputs; + unsigned int num_hdmi_outputs; u8 output_type[MAX_OUTPUTS]; u8 output_name_counter[MAX_OUTPUTS]; bool has_audio_inputs; @@ -237,6 +227,7 @@ struct vivid_dev { struct v4l2_ctrl *ctrl_dv_timings_signal_mode; struct v4l2_ctrl *ctrl_dv_timings; }; + struct v4l2_ctrl *ctrl_display_present; struct v4l2_ctrl *ctrl_has_crop_cap; struct v4l2_ctrl *ctrl_has_compose_cap; struct v4l2_ctrl *ctrl_has_scaler_cap; @@ -245,6 +236,11 @@ struct vivid_dev { struct v4l2_ctrl *ctrl_has_scaler_out; struct v4l2_ctrl *ctrl_tx_mode; struct v4l2_ctrl *ctrl_tx_rgb_range; + struct v4l2_ctrl *ctrl_tx_edid_present; + struct v4l2_ctrl *ctrl_tx_hotplug; + struct v4l2_ctrl *ctrl_tx_rxsense; + + struct v4l2_ctrl *ctrl_rx_power_present; struct v4l2_ctrl *radio_tx_rds_pi; struct v4l2_ctrl *radio_tx_rds_pty; @@ -299,23 +295,24 @@ struct vivid_dev { bool time_wrap; u64 time_wrap_offset; unsigned perc_dropped_buffers; - enum vivid_signal_mode std_signal_mode; - unsigned query_std_last; - v4l2_std_id query_std; - enum tpg_video_aspect std_aspect_ratio; + enum vivid_signal_mode std_signal_mode[MAX_INPUTS]; + unsigned int query_std_last[MAX_INPUTS]; + v4l2_std_id query_std[MAX_INPUTS]; + enum tpg_video_aspect std_aspect_ratio[MAX_INPUTS]; - enum vivid_signal_mode dv_timings_signal_mode; + enum vivid_signal_mode dv_timings_signal_mode[MAX_INPUTS]; char **query_dv_timings_qmenu; char *query_dv_timings_qmenu_strings; unsigned query_dv_timings_size; - unsigned query_dv_timings_last; - unsigned query_dv_timings; - enum tpg_video_aspect dv_timings_aspect_ratio; + unsigned int query_dv_timings_last[MAX_INPUTS]; + unsigned int query_dv_timings[MAX_INPUTS]; + enum tpg_video_aspect dv_timings_aspect_ratio[MAX_INPUTS]; /* Input */ unsigned input; - v4l2_std_id std_cap; - struct v4l2_dv_timings dv_timings_cap; + v4l2_std_id std_cap[MAX_INPUTS]; + struct v4l2_dv_timings dv_timings_cap[MAX_INPUTS]; + int dv_timings_cap_sel[MAX_INPUTS]; u32 service_set_cap; struct vivid_vbi_gen_data vbi_gen; u8 *edid; @@ -328,6 +325,8 @@ struct vivid_dev { unsigned tv_field_cap; unsigned tv_audio_input; + u32 power_present; + /* Capture Overlay */ struct v4l2_framebuffer fb_cap; struct v4l2_fh *overlay_cap_owner; @@ -360,6 +359,7 @@ struct vivid_dev { u8 *scaled_line; u8 *blended_line; unsigned cur_scaled_line; + bool display_present[MAX_OUTPUTS]; /* Output Overlay */ void *fb_vbase_out; diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c index 4cd526ff248b..3e916c8befb7 100644 --- a/drivers/media/platform/vivid/vivid-ctrls.c +++ b/drivers/media/platform/vivid/vivid-ctrls.c @@ -18,6 +18,7 @@ #include "vivid-radio-common.h" #include "vivid-osd.h" #include "vivid-ctrls.h" +#include "vivid-cec.h" #define VIVID_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0xf000) #define VIVID_CID_BUTTON (VIVID_CID_CUSTOM_BASE + 0) @@ -68,6 +69,7 @@ #define VIVID_CID_PERCENTAGE_FILL (VIVID_CID_VIVID_BASE + 41) #define VIVID_CID_REDUCED_FPS (VIVID_CID_VIVID_BASE + 42) #define VIVID_CID_HSV_ENC (VIVID_CID_VIVID_BASE + 43) +#define VIVID_CID_DISPLAY_PRESENT (VIVID_CID_VIVID_BASE + 44) #define VIVID_CID_STD_SIGNAL_MODE (VIVID_CID_VIVID_BASE + 60) #define VIVID_CID_STANDARD (VIVID_CID_VIVID_BASE + 61) @@ -357,7 +359,7 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) V4L2_COLORSPACE_470_SYSTEM_BG, }; struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vid_cap); - unsigned i; + unsigned int i, j; switch (ctrl->id) { case VIVID_CID_TEST_PATTERN: @@ -463,20 +465,35 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) tpg_s_show_square(&dev->tpg, ctrl->val); break; case VIVID_CID_STD_ASPECT_RATIO: - dev->std_aspect_ratio = ctrl->val; + dev->std_aspect_ratio[dev->input] = ctrl->val; tpg_s_video_aspect(&dev->tpg, vivid_get_video_aspect(dev)); break; case VIVID_CID_DV_TIMINGS_SIGNAL_MODE: - dev->dv_timings_signal_mode = dev->ctrl_dv_timings_signal_mode->val; - if (dev->dv_timings_signal_mode == SELECTED_DV_TIMINGS) - dev->query_dv_timings = dev->ctrl_dv_timings->val; + dev->dv_timings_signal_mode[dev->input] = + dev->ctrl_dv_timings_signal_mode->val; + dev->query_dv_timings[dev->input] = dev->ctrl_dv_timings->val; + + dev->power_present = 0; + for (i = 0, j = 0; + i < ARRAY_SIZE(dev->dv_timings_signal_mode); + i++) + if (dev->input_type[i] == HDMI) { + if (dev->dv_timings_signal_mode[i] != NO_SIGNAL) + dev->power_present |= (1 << j); + j++; + } + __v4l2_ctrl_s_ctrl(dev->ctrl_rx_power_present, + dev->power_present); + v4l2_ctrl_activate(dev->ctrl_dv_timings, - dev->dv_timings_signal_mode == SELECTED_DV_TIMINGS); + dev->dv_timings_signal_mode[dev->input] == + SELECTED_DV_TIMINGS); + vivid_update_quality(dev); vivid_send_source_change(dev, HDMI); break; case VIVID_CID_DV_TIMINGS_ASPECT_RATIO: - dev->dv_timings_aspect_ratio = ctrl->val; + dev->dv_timings_aspect_ratio[dev->input] = ctrl->val; tpg_s_video_aspect(&dev->tpg, vivid_get_video_aspect(dev)); break; case VIVID_CID_TSTAMP_SRC: @@ -908,6 +925,8 @@ static int vivid_vid_out_s_ctrl(struct v4l2_ctrl *ctrl) { struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vid_out); struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt; + u32 display_present = 0; + unsigned int i, j, bus_idx; switch (ctrl->id) { case VIVID_CID_HAS_CROP_OUT: @@ -941,6 +960,37 @@ static int vivid_vid_out_s_ctrl(struct v4l2_ctrl *ctrl) if (dev->loop_video) vivid_send_source_change(dev, HDMI); break; + case VIVID_CID_DISPLAY_PRESENT: + if (dev->output_type[dev->output] != HDMI) + break; + + dev->display_present[dev->output] = ctrl->val; + for (i = 0, j = 0; i < dev->num_outputs; i++) + if (dev->output_type[i] == HDMI) + display_present |= + dev->display_present[i] << j++; + + __v4l2_ctrl_s_ctrl(dev->ctrl_tx_rxsense, display_present); + + if (dev->edid_blocks) { + __v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, + display_present); + __v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, + display_present); + } + + bus_idx = dev->cec_output2bus_map[dev->output]; + if (!dev->cec_tx_adap[bus_idx]) + break; + + if (ctrl->val && dev->edid_blocks) + cec_s_phys_addr(dev->cec_tx_adap[bus_idx], + dev->cec_tx_adap[bus_idx]->phys_addr, + false); + else + cec_phys_addr_invalidate(dev->cec_tx_adap[bus_idx]); + + break; } return 0; } @@ -979,6 +1029,15 @@ static const struct v4l2_ctrl_config vivid_ctrl_has_scaler_out = { .step = 1, }; +static const struct v4l2_ctrl_config vivid_ctrl_display_present = { + .ops = &vivid_vid_out_ctrl_ops, + .id = VIVID_CID_DISPLAY_PRESENT, + .name = "Display Present", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .def = 1, + .step = 1, +}; /* Streaming Controls */ @@ -1127,10 +1186,14 @@ static int vivid_sdtv_cap_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case VIVID_CID_STD_SIGNAL_MODE: - dev->std_signal_mode = dev->ctrl_std_signal_mode->val; - if (dev->std_signal_mode == SELECTED_STD) - dev->query_std = vivid_standard[dev->ctrl_standard->val]; - v4l2_ctrl_activate(dev->ctrl_standard, dev->std_signal_mode == SELECTED_STD); + dev->std_signal_mode[dev->input] = + dev->ctrl_std_signal_mode->val; + if (dev->std_signal_mode[dev->input] == SELECTED_STD) + dev->query_std[dev->input] = + vivid_standard[dev->ctrl_standard->val]; + v4l2_ctrl_activate(dev->ctrl_standard, + dev->std_signal_mode[dev->input] == + SELECTED_STD); vivid_update_quality(dev); vivid_send_source_change(dev, TV); vivid_send_source_change(dev, SVID); @@ -1549,7 +1612,7 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, v4l2_ctrl_new_custom(hdl_vbi_cap, &vivid_ctrl_vbi_cap_interlaced, NULL); } - if (has_hdmi && dev->has_vid_cap) { + if (dev->num_hdmi_inputs) { dev->ctrl_dv_timings_signal_mode = v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_dv_timings_signal_mode, NULL); @@ -1569,8 +1632,13 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, &vivid_vid_cap_ctrl_ops, V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, 0, V4L2_DV_RGB_RANGE_AUTO); + dev->ctrl_rx_power_present = v4l2_ctrl_new_std(hdl_vid_cap, + NULL, V4L2_CID_DV_RX_POWER_PRESENT, 0, + (2 << (dev->num_hdmi_inputs - 1)) - 1, 0, + (2 << (dev->num_hdmi_inputs - 1)) - 1); + } - if (has_hdmi && dev->has_vid_out) { + if (dev->num_hdmi_outputs) { /* * We aren't doing anything with this at the moment, but * HDMI outputs typically have this controls. @@ -1581,6 +1649,20 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, dev->ctrl_tx_mode = v4l2_ctrl_new_std_menu(hdl_vid_out, NULL, V4L2_CID_DV_TX_MODE, V4L2_DV_TX_MODE_HDMI, 0, V4L2_DV_TX_MODE_HDMI); + dev->ctrl_display_present = v4l2_ctrl_new_custom(hdl_vid_out, + &vivid_ctrl_display_present, NULL); + dev->ctrl_tx_hotplug = v4l2_ctrl_new_std(hdl_vid_out, + NULL, V4L2_CID_DV_TX_HOTPLUG, 0, + (2 << (dev->num_hdmi_outputs - 1)) - 1, 0, + (2 << (dev->num_hdmi_outputs - 1)) - 1); + dev->ctrl_tx_rxsense = v4l2_ctrl_new_std(hdl_vid_out, + NULL, V4L2_CID_DV_TX_RXSENSE, 0, + (2 << (dev->num_hdmi_outputs - 1)) - 1, 0, + (2 << (dev->num_hdmi_outputs - 1)) - 1); + dev->ctrl_tx_edid_present = v4l2_ctrl_new_std(hdl_vid_out, + NULL, V4L2_CID_DV_TX_EDID_PRESENT, 0, + (2 << (dev->num_hdmi_outputs - 1)) - 1, 0, + (2 << (dev->num_hdmi_outputs - 1)) - 1); } if ((dev->has_vid_cap && dev->has_vid_out) || (dev->has_vbi_cap && dev->has_vbi_out)) diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.c b/drivers/media/platform/vivid/vivid-kthread-cap.c index f8006a30c12f..6cf495a7d5cc 100644 --- a/drivers/media/platform/vivid/vivid-kthread-cap.c +++ b/drivers/media/platform/vivid/vivid-kthread-cap.c @@ -43,7 +43,7 @@ static inline v4l2_std_id vivid_get_std_cap(const struct vivid_dev *dev) { if (vivid_is_sdtv_cap(dev)) - return dev->std_cap; + return dev->std_cap[dev->input]; return 0; } @@ -408,7 +408,7 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) unsigned factor = V4L2_FIELD_HAS_T_OR_B(dev->field_cap) ? 2 : 1; unsigned line_height = 16 / factor; bool is_tv = vivid_is_sdtv_cap(dev); - bool is_60hz = is_tv && (dev->std_cap & V4L2_STD_525_60); + bool is_60hz = is_tv && (dev->std_cap[dev->input] & V4L2_STD_525_60); unsigned p; int line = 1; u8 *basep[TPG_MAX_PLANES][2]; @@ -419,9 +419,9 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) if (dev->loop_video && dev->can_loop_video && ((vivid_is_svid_cap(dev) && - !VIVID_INVALID_SIGNAL(dev->std_signal_mode)) || + !VIVID_INVALID_SIGNAL(dev->std_signal_mode[dev->input])) || (vivid_is_hdmi_cap(dev) && - !VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode)))) + !VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode[dev->input])))) is_loop = true; buf->vb.sequence = dev->vid_cap_seq_count; diff --git a/drivers/media/platform/vivid/vivid-osd.c b/drivers/media/platform/vivid/vivid-osd.c index 1a89593b0c86..f2e789bdf4a6 100644 --- a/drivers/media/platform/vivid/vivid-osd.c +++ b/drivers/media/platform/vivid/vivid-osd.c @@ -155,7 +155,7 @@ static int _vivid_fb_check_var(struct fb_var_screeninfo *var, struct vivid_dev * var->nonstd = 0; var->vmode &= ~FB_VMODE_MASK; - var->vmode = FB_VMODE_NONINTERLACED; + var->vmode |= FB_VMODE_NONINTERLACED; /* Dummy values */ var->hsync_len = 24; diff --git a/drivers/media/platform/vivid/vivid-vbi-cap.c b/drivers/media/platform/vivid/vivid-vbi-cap.c index 40ecd7902b56..1a9348eea781 100644 --- a/drivers/media/platform/vivid/vivid-vbi-cap.c +++ b/drivers/media/platform/vivid/vivid-vbi-cap.c @@ -18,7 +18,7 @@ static void vivid_sliced_vbi_cap_fill(struct vivid_dev *dev, unsigned seqnr) { struct vivid_vbi_gen_data *vbi_gen = &dev->vbi_gen; - bool is_60hz = dev->std_cap & V4L2_STD_525_60; + bool is_60hz = dev->std_cap[dev->input] & V4L2_STD_525_60; vivid_vbi_gen_sliced(vbi_gen, is_60hz, seqnr); @@ -65,7 +65,7 @@ static void vivid_sliced_vbi_cap_fill(struct vivid_dev *dev, unsigned seqnr) static void vivid_g_fmt_vbi_cap(struct vivid_dev *dev, struct v4l2_vbi_format *vbi) { - bool is_60hz = dev->std_cap & V4L2_STD_525_60; + bool is_60hz = dev->std_cap[dev->input] & V4L2_STD_525_60; vbi->sampling_rate = 27000000; vbi->offset = 24; @@ -93,7 +93,7 @@ void vivid_raw_vbi_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf) memset(vbuf, 0x10, vb2_plane_size(&buf->vb.vb2_buf, 0)); - if (!VIVID_INVALID_SIGNAL(dev->std_signal_mode)) + if (!VIVID_INVALID_SIGNAL(dev->std_signal_mode[dev->input])) vivid_vbi_gen_raw(&dev->vbi_gen, &vbi, vbuf); } @@ -111,7 +111,7 @@ void vivid_sliced_vbi_cap_process(struct vivid_dev *dev, vivid_sliced_vbi_cap_fill(dev, buf->vb.sequence); memset(vbuf, 0, vb2_plane_size(&buf->vb.vb2_buf, 0)); - if (!VIVID_INVALID_SIGNAL(dev->std_signal_mode)) { + if (!VIVID_INVALID_SIGNAL(dev->std_signal_mode[dev->input])) { unsigned i; for (i = 0; i < 25; i++) @@ -124,7 +124,7 @@ static int vbi_cap_queue_setup(struct vb2_queue *vq, unsigned sizes[], struct device *alloc_devs[]) { struct vivid_dev *dev = vb2_get_drv_priv(vq); - bool is_60hz = dev->std_cap & V4L2_STD_525_60; + bool is_60hz = dev->std_cap[dev->input] & V4L2_STD_525_60; unsigned size = vq->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE ? 36 * sizeof(struct v4l2_sliced_vbi_data) : 1440 * 2 * (is_60hz ? 12 : 18); @@ -144,7 +144,7 @@ static int vbi_cap_queue_setup(struct vb2_queue *vq, static int vbi_cap_buf_prepare(struct vb2_buffer *vb) { struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); - bool is_60hz = dev->std_cap & V4L2_STD_525_60; + bool is_60hz = dev->std_cap[dev->input] & V4L2_STD_525_60; unsigned size = vb->vb2_queue->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE ? 36 * sizeof(struct v4l2_sliced_vbi_data) : 1440 * 2 * (is_60hz ? 12 : 18); @@ -302,7 +302,7 @@ int vidioc_try_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_forma { struct vivid_dev *dev = video_drvdata(file); struct v4l2_sliced_vbi_format *vbi = &fmt->fmt.sliced; - bool is_60hz = dev->std_cap & V4L2_STD_525_60; + bool is_60hz = dev->std_cap[dev->input] & V4L2_STD_525_60; u32 service_set = vbi->service_set; if (!vivid_is_sdtv_cap(dev) || !dev->has_sliced_vbi_cap) @@ -337,7 +337,7 @@ int vidioc_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced_vbi_ bool is_60hz; if (vdev->vfl_dir == VFL_DIR_RX) { - is_60hz = dev->std_cap & V4L2_STD_525_60; + is_60hz = dev->std_cap[dev->input] & V4L2_STD_525_60; if (!vivid_is_sdtv_cap(dev) || !dev->has_sliced_vbi_cap || cap->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) return -EINVAL; diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c index 530ac8decb25..8cbaa0c998ed 100644 --- a/drivers/media/platform/vivid/vivid-vid-cap.c +++ b/drivers/media/platform/vivid/vivid-vid-cap.c @@ -21,11 +21,6 @@ #include "vivid-kthread-cap.h" #include "vivid-vid-cap.h" -/* timeperframe: min/max and default */ -static const struct v4l2_fract - tpf_min = {.numerator = 1, .denominator = FPS_MAX}, - tpf_max = {.numerator = FPS_MAX, .denominator = 1}; - static const struct vivid_fmt formats_ovl[] = { { .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ @@ -196,7 +191,7 @@ static void vid_cap_buf_finish(struct vb2_buffer *vb) * test this. */ vbuf->flags |= V4L2_BUF_FLAG_TIMECODE; - if (dev->std_cap & V4L2_STD_525_60) + if (dev->std_cap[dev->input] & V4L2_STD_525_60) fps = 30; tc->type = (fps == 30) ? V4L2_TC_TYPE_30FPS : V4L2_TC_TYPE_25FPS; tc->flags = 0; @@ -299,11 +294,13 @@ void vivid_update_quality(struct vivid_dev *dev) tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE, 0); return; } - if (vivid_is_hdmi_cap(dev) && VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode)) { + if (vivid_is_hdmi_cap(dev) && + VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode[dev->input])) { tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE, 0); return; } - if (vivid_is_sdtv_cap(dev) && VIVID_INVALID_SIGNAL(dev->std_signal_mode)) { + if (vivid_is_sdtv_cap(dev) && + VIVID_INVALID_SIGNAL(dev->std_signal_mode[dev->input])) { tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE, 0); return; } @@ -358,10 +355,10 @@ static enum tpg_quality vivid_get_quality(struct vivid_dev *dev, s32 *afc) enum tpg_video_aspect vivid_get_video_aspect(const struct vivid_dev *dev) { if (vivid_is_sdtv_cap(dev)) - return dev->std_aspect_ratio; + return dev->std_aspect_ratio[dev->input]; if (vivid_is_hdmi_cap(dev)) - return dev->dv_timings_aspect_ratio; + return dev->dv_timings_aspect_ratio[dev->input]; return TPG_VIDEO_ASPECT_IMAGE; } @@ -369,7 +366,7 @@ enum tpg_video_aspect vivid_get_video_aspect(const struct vivid_dev *dev) static enum tpg_pixel_aspect vivid_get_pixel_aspect(const struct vivid_dev *dev) { if (vivid_is_sdtv_cap(dev)) - return (dev->std_cap & V4L2_STD_525_60) ? + return (dev->std_cap[dev->input] & V4L2_STD_525_60) ? TPG_PIXEL_ASPECT_NTSC : TPG_PIXEL_ASPECT_PAL; if (vivid_is_hdmi_cap(dev) && @@ -386,7 +383,7 @@ static enum tpg_pixel_aspect vivid_get_pixel_aspect(const struct vivid_dev *dev) */ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls) { - struct v4l2_bt_timings *bt = &dev->dv_timings_cap.bt; + struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt; unsigned size; u64 pixelclock; @@ -403,7 +400,7 @@ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls) case SVID: dev->field_cap = dev->tv_field_cap; dev->src_rect.width = 720; - if (dev->std_cap & V4L2_STD_525_60) { + if (dev->std_cap[dev->input] & V4L2_STD_525_60) { dev->src_rect.height = 480; dev->timeperframe_vid_cap = (struct v4l2_fract) { 1001, 30000 }; dev->service_set_cap = V4L2_SLICED_CAPTION_525; @@ -486,8 +483,8 @@ static enum v4l2_field vivid_field_cap(struct vivid_dev *dev, enum v4l2_field fi } } if (vivid_is_hdmi_cap(dev)) - return dev->dv_timings_cap.bt.interlaced ? V4L2_FIELD_ALTERNATE : - V4L2_FIELD_NONE; + return dev->dv_timings_cap[dev->input].bt.interlaced ? + V4L2_FIELD_ALTERNATE : V4L2_FIELD_NONE; return V4L2_FIELD_NONE; } @@ -586,7 +583,7 @@ int vivid_try_fmt_vid_cap(struct file *file, void *priv, h = sz->height; } else if (vivid_is_sdtv_cap(dev)) { w = 720; - h = (dev->std_cap & V4L2_STD_525_60) ? 480 : 576; + h = (dev->std_cap[dev->input] & V4L2_STD_525_60) ? 480 : 576; } else { w = dev->src_rect.width; h = dev->src_rect.height; @@ -1310,10 +1307,10 @@ int vidioc_enum_input(struct file *file, void *priv, dev->input_name_counter[inp->index]); inp->capabilities = V4L2_IN_CAP_DV_TIMINGS; if (dev->edid_blocks == 0 || - dev->dv_timings_signal_mode == NO_SIGNAL) + dev->dv_timings_signal_mode[dev->input] == NO_SIGNAL) inp->status |= V4L2_IN_ST_NO_SIGNAL; - else if (dev->dv_timings_signal_mode == NO_LOCK || - dev->dv_timings_signal_mode == OUT_OF_RANGE) + else if (dev->dv_timings_signal_mode[dev->input] == NO_LOCK || + dev->dv_timings_signal_mode[dev->input] == OUT_OF_RANGE) inp->status |= V4L2_IN_ST_NO_H_LOCK; break; } @@ -1322,9 +1319,9 @@ int vidioc_enum_input(struct file *file, void *priv, if (dev->sensor_vflip) inp->status |= V4L2_IN_ST_VFLIP; if (dev->input == inp->index && vivid_is_sdtv_cap(dev)) { - if (dev->std_signal_mode == NO_SIGNAL) { + if (dev->std_signal_mode[dev->input] == NO_SIGNAL) { inp->status |= V4L2_IN_ST_NO_SIGNAL; - } else if (dev->std_signal_mode == NO_LOCK) { + } else if (dev->std_signal_mode[dev->input] == NO_LOCK) { inp->status |= V4L2_IN_ST_NO_H_LOCK; } else if (vivid_is_tv_cap(dev)) { switch (tpg_g_quality(&dev->tpg)) { @@ -1353,7 +1350,7 @@ int vidioc_g_input(struct file *file, void *priv, unsigned *i) int vidioc_s_input(struct file *file, void *priv, unsigned i) { struct vivid_dev *dev = video_drvdata(file); - struct v4l2_bt_timings *bt = &dev->dv_timings_cap.bt; + struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt; unsigned brightness; if (i >= dev->num_inputs) @@ -1407,6 +1404,29 @@ int vidioc_s_input(struct file *file, void *priv, unsigned i) v4l2_ctrl_modify_range(dev->brightness, 128 * i, 255 + 128 * i, 1, 128 + 128 * i); v4l2_ctrl_s_ctrl(dev->brightness, brightness); + + /* Restore per-input states. */ + v4l2_ctrl_activate(dev->ctrl_dv_timings_signal_mode, + vivid_is_hdmi_cap(dev)); + v4l2_ctrl_activate(dev->ctrl_dv_timings, vivid_is_hdmi_cap(dev) && + dev->dv_timings_signal_mode[dev->input] == + SELECTED_DV_TIMINGS); + v4l2_ctrl_activate(dev->ctrl_std_signal_mode, vivid_is_sdtv_cap(dev)); + v4l2_ctrl_activate(dev->ctrl_standard, vivid_is_sdtv_cap(dev) && + dev->std_signal_mode[dev->input]); + + if (vivid_is_hdmi_cap(dev)) { + v4l2_ctrl_s_ctrl(dev->ctrl_dv_timings_signal_mode, + dev->dv_timings_signal_mode[dev->input]); + v4l2_ctrl_s_ctrl(dev->ctrl_dv_timings, + dev->query_dv_timings[dev->input]); + } else if (vivid_is_sdtv_cap(dev)) { + v4l2_ctrl_s_ctrl(dev->ctrl_std_signal_mode, + dev->std_signal_mode[dev->input]); + v4l2_ctrl_s_ctrl(dev->ctrl_standard, + dev->std_signal_mode[dev->input]); + } + return 0; } @@ -1499,8 +1519,9 @@ int vivid_video_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) } else if (qual == TPG_QUAL_GRAY) { vt->rxsubchans = V4L2_TUNER_SUB_MONO; } else { - unsigned channel_nr = dev->tv_freq / (6 * 16); - unsigned options = (dev->std_cap & V4L2_STD_NTSC_M) ? 4 : 3; + unsigned int channel_nr = dev->tv_freq / (6 * 16); + unsigned int options = + (dev->std_cap[dev->input] & V4L2_STD_NTSC_M) ? 4 : 3; switch (channel_nr % options) { case 0: @@ -1510,7 +1531,7 @@ int vivid_video_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) vt->rxsubchans = V4L2_TUNER_SUB_STEREO; break; case 2: - if (dev->std_cap & V4L2_STD_NTSC_M) + if (dev->std_cap[dev->input] & V4L2_STD_NTSC_M) vt->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_SAP; else vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; @@ -1567,23 +1588,25 @@ const char * const vivid_ctrl_standard_strings[] = { int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *id) { struct vivid_dev *dev = video_drvdata(file); + unsigned int last = dev->query_std_last[dev->input]; if (!vivid_is_sdtv_cap(dev)) return -ENODATA; - if (dev->std_signal_mode == NO_SIGNAL || - dev->std_signal_mode == NO_LOCK) { + if (dev->std_signal_mode[dev->input] == NO_SIGNAL || + dev->std_signal_mode[dev->input] == NO_LOCK) { *id = V4L2_STD_UNKNOWN; return 0; } if (vivid_is_tv_cap(dev) && tpg_g_quality(&dev->tpg) == TPG_QUAL_NOISE) { *id = V4L2_STD_UNKNOWN; - } else if (dev->std_signal_mode == CURRENT_STD) { - *id = dev->std_cap; - } else if (dev->std_signal_mode == SELECTED_STD) { - *id = dev->query_std; + } else if (dev->std_signal_mode[dev->input] == CURRENT_STD) { + *id = dev->std_cap[dev->input]; + } else if (dev->std_signal_mode[dev->input] == SELECTED_STD) { + *id = dev->query_std[dev->input]; } else { - *id = vivid_standard[dev->query_std_last]; - dev->query_std_last = (dev->query_std_last + 1) % ARRAY_SIZE(vivid_standard); + *id = vivid_standard[last]; + dev->query_std_last[dev->input] = + (last + 1) % ARRAY_SIZE(vivid_standard); } return 0; @@ -1595,11 +1618,11 @@ int vivid_vid_cap_s_std(struct file *file, void *priv, v4l2_std_id id) if (!vivid_is_sdtv_cap(dev)) return -ENODATA; - if (dev->std_cap == id) + if (dev->std_cap[dev->input] == id) return 0; if (vb2_is_busy(&dev->vb_vid_cap_q) || vb2_is_busy(&dev->vb_vbi_cap_q)) return -EBUSY; - dev->std_cap = id; + dev->std_cap[dev->input] = id; vivid_update_format_cap(dev, false); return 0; } @@ -1676,12 +1699,13 @@ int vivid_vid_cap_s_dv_timings(struct file *file, void *_fh, !valid_cvt_gtf_timings(timings)) return -EINVAL; - if (v4l2_match_dv_timings(timings, &dev->dv_timings_cap, 0, false)) + if (v4l2_match_dv_timings(timings, &dev->dv_timings_cap[dev->input], + 0, false)) return 0; if (vb2_is_busy(&dev->vb_vid_cap_q)) return -EBUSY; - dev->dv_timings_cap = *timings; + dev->dv_timings_cap[dev->input] = *timings; vivid_update_format_cap(dev, false); return 0; } @@ -1690,26 +1714,31 @@ int vidioc_query_dv_timings(struct file *file, void *_fh, struct v4l2_dv_timings *timings) { struct vivid_dev *dev = video_drvdata(file); + unsigned int input = dev->input; + unsigned int last = dev->query_dv_timings_last[input]; if (!vivid_is_hdmi_cap(dev)) return -ENODATA; - if (dev->dv_timings_signal_mode == NO_SIGNAL || + if (dev->dv_timings_signal_mode[input] == NO_SIGNAL || dev->edid_blocks == 0) return -ENOLINK; - if (dev->dv_timings_signal_mode == NO_LOCK) + if (dev->dv_timings_signal_mode[input] == NO_LOCK) return -ENOLCK; - if (dev->dv_timings_signal_mode == OUT_OF_RANGE) { + if (dev->dv_timings_signal_mode[input] == OUT_OF_RANGE) { timings->bt.pixelclock = vivid_dv_timings_cap.bt.max_pixelclock * 2; return -ERANGE; } - if (dev->dv_timings_signal_mode == CURRENT_DV_TIMINGS) { - *timings = dev->dv_timings_cap; - } else if (dev->dv_timings_signal_mode == SELECTED_DV_TIMINGS) { - *timings = v4l2_dv_timings_presets[dev->query_dv_timings]; + if (dev->dv_timings_signal_mode[input] == CURRENT_DV_TIMINGS) { + *timings = dev->dv_timings_cap[input]; + } else if (dev->dv_timings_signal_mode[input] == + SELECTED_DV_TIMINGS) { + *timings = + v4l2_dv_timings_presets[dev->query_dv_timings[input]]; } else { - *timings = v4l2_dv_timings_presets[dev->query_dv_timings_last]; - dev->query_dv_timings_last = (dev->query_dv_timings_last + 1) % - dev->query_dv_timings_size; + *timings = + v4l2_dv_timings_presets[last]; + dev->query_dv_timings_last[input] = + (last + 1) % dev->query_dv_timings_size; } return 0; } @@ -1719,7 +1748,8 @@ int vidioc_s_edid(struct file *file, void *_fh, { struct vivid_dev *dev = video_drvdata(file); u16 phys_addr; - unsigned int i; + u32 display_present = 0; + unsigned int i, j; int ret; memset(edid->reserved, 0, sizeof(edid->reserved)); @@ -1729,6 +1759,8 @@ int vidioc_s_edid(struct file *file, void *_fh, return -EINVAL; if (edid->blocks == 0) { dev->edid_blocks = 0; + v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, 0); + v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, 0); phys_addr = CEC_PHYS_ADDR_INVALID; goto set_phys_addr; } @@ -1747,13 +1779,23 @@ int vidioc_s_edid(struct file *file, void *_fh, dev->edid_blocks = edid->blocks; memcpy(dev->edid, edid->edid, edid->blocks * 128); + for (i = 0, j = 0; i < dev->num_outputs; i++) + if (dev->output_type[i] == HDMI) + display_present |= + dev->display_present[i] << j++; + + v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, display_present); + v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, display_present); + set_phys_addr: /* TODO: a proper hotplug detect cycle should be emulated here */ cec_s_phys_addr(dev->cec_rx_adap, phys_addr, false); for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) cec_s_phys_addr(dev->cec_tx_adap[i], - v4l2_phys_addr_for_input(phys_addr, i + 1), + dev->display_present[i] ? + v4l2_phys_addr_for_input(phys_addr, i + 1) : + CEC_PHYS_ADDR_INVALID, false); return 0; } @@ -1865,8 +1907,6 @@ int vivid_vid_cap_s_parm(struct file *file, void *priv, i = ival_sz - 1; dev->webcam_ival_idx = i; tpf = webcam_intervals[dev->webcam_ival_idx]; - tpf = V4L2_FRACT_COMPARE(tpf, <, tpf_min) ? tpf_min : tpf; - tpf = V4L2_FRACT_COMPARE(tpf, >, tpf_max) ? tpf_max : tpf; /* resync the thread's timings */ dev->cap_seq_resync = true; diff --git a/drivers/media/platform/vivid/vivid-vid-common.c b/drivers/media/platform/vivid/vivid-vid-common.c index 74b83bcc6119..1f33eb1a76b6 100644 --- a/drivers/media/platform/vivid/vivid-vid-common.c +++ b/drivers/media/platform/vivid/vivid-vid-common.c @@ -645,7 +645,7 @@ bool vivid_vid_can_loop(struct vivid_dev *dev) dev->field_cap == V4L2_FIELD_SEQ_BT) return false; if (vivid_is_svid_cap(dev) && vivid_is_svid_out(dev)) { - if (!(dev->std_cap & V4L2_STD_525_60) != + if (!(dev->std_cap[dev->input] & V4L2_STD_525_60) != !(dev->std_out & V4L2_STD_525_60)) return false; return true; @@ -797,26 +797,6 @@ int vivid_enum_fmt_vid(struct file *file, void *priv, return 0; } -int vidioc_enum_fmt_vid_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct vivid_dev *dev = video_drvdata(file); - - if (!dev->multiplanar) - return -ENOTTY; - return vivid_enum_fmt_vid(file, priv, f); -} - -int vidioc_enum_fmt_vid(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct vivid_dev *dev = video_drvdata(file); - - if (dev->multiplanar) - return -ENOTTY; - return vivid_enum_fmt_vid(file, priv, f); -} - int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) { struct vivid_dev *dev = video_drvdata(file); @@ -825,7 +805,7 @@ int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) if (vdev->vfl_dir == VFL_DIR_RX) { if (!vivid_is_sdtv_cap(dev)) return -ENODATA; - *id = dev->std_cap; + *id = dev->std_cap[dev->input]; } else { if (!vivid_is_svid_out(dev)) return -ENODATA; @@ -843,7 +823,7 @@ int vidioc_g_dv_timings(struct file *file, void *_fh, if (vdev->vfl_dir == VFL_DIR_RX) { if (!vivid_is_hdmi_cap(dev)) return -ENODATA; - *timings = dev->dv_timings_cap; + *timings = dev->dv_timings_cap[dev->input]; } else { if (!vivid_is_hdmi_out(dev)) return -ENODATA; @@ -907,6 +887,8 @@ int vidioc_g_edid(struct file *file, void *_fh, return -EINVAL; if (dev->output_type[edid->pad] != HDMI) return -EINVAL; + if (!dev->display_present[edid->pad]) + return -ENODATA; bus_idx = dev->cec_output2bus_map[edid->pad]; adap = dev->cec_tx_adap[bus_idx]; } diff --git a/drivers/media/platform/vivid/vivid-vid-common.h b/drivers/media/platform/vivid/vivid-vid-common.h index 29b6c0b40a1b..d908d9725283 100644 --- a/drivers/media/platform/vivid/vivid-vid-common.h +++ b/drivers/media/platform/vivid/vivid-vid-common.h @@ -28,8 +28,6 @@ void vivid_send_source_change(struct vivid_dev *dev, unsigned type); int vivid_vid_adjust_sel(unsigned flags, struct v4l2_rect *r); int vivid_enum_fmt_vid(struct file *file, void *priv, struct v4l2_fmtdesc *f); -int vidioc_enum_fmt_vid_mplane(struct file *file, void *priv, struct v4l2_fmtdesc *f); -int vidioc_enum_fmt_vid(struct file *file, void *priv, struct v4l2_fmtdesc *f); int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id); int vidioc_g_dv_timings(struct file *file, void *_fh, struct v4l2_dv_timings *timings); int vidioc_enum_dv_timings(struct file *file, void *_fh, struct v4l2_enum_dv_timings *timings); diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c index 9350ca65dd91..148b663a6075 100644 --- a/drivers/media/platform/vivid/vivid-vid-out.c +++ b/drivers/media/platform/vivid/vivid-vid-out.c @@ -1094,6 +1094,12 @@ int vidioc_s_output(struct file *file, void *priv, unsigned o) dev->vbi_out_dev.tvnorms = dev->vid_out_dev.tvnorms; vivid_update_format_out(dev); + + v4l2_ctrl_activate(dev->ctrl_display_present, vivid_is_hdmi_out(dev)); + if (vivid_is_hdmi_out(dev)) + v4l2_ctrl_s_ctrl(dev->ctrl_display_present, + dev->display_present[dev->output]); + return 0; } |