162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2011 Atmel Corporation 462306a36Sopenharmony_ci * Josh Wu, <josh.wu@atmel.com> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Based on previous work by Lars Haring, <lars.haring@atmel.com> 762306a36Sopenharmony_ci * and Sedji Gaouaou 862306a36Sopenharmony_ci * Based on the bttv driver for Bt848 with respective copyright holders 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/clk.h> 1262306a36Sopenharmony_ci#include <linux/completion.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/fs.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/of_graph.h> 2062306a36Sopenharmony_ci#include <linux/platform_device.h> 2162306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/of.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/videodev2.h> 2662306a36Sopenharmony_ci#include <media/v4l2-ctrls.h> 2762306a36Sopenharmony_ci#include <media/v4l2-device.h> 2862306a36Sopenharmony_ci#include <media/v4l2-dev.h> 2962306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 3062306a36Sopenharmony_ci#include <media/v4l2-event.h> 3162306a36Sopenharmony_ci#include <media/v4l2-fwnode.h> 3262306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 3362306a36Sopenharmony_ci#include <media/v4l2-image-sizes.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include "atmel-isi.h" 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define MAX_SUPPORT_WIDTH 2048U 3862306a36Sopenharmony_ci#define MAX_SUPPORT_HEIGHT 2048U 3962306a36Sopenharmony_ci#define MIN_FRAME_RATE 15 4062306a36Sopenharmony_ci#define FRAME_INTERVAL_MILLI_SEC (1000 / MIN_FRAME_RATE) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* Frame buffer descriptor */ 4362306a36Sopenharmony_cistruct fbd { 4462306a36Sopenharmony_ci /* Physical address of the frame buffer */ 4562306a36Sopenharmony_ci u32 fb_address; 4662306a36Sopenharmony_ci /* DMA Control Register(only in HISI2) */ 4762306a36Sopenharmony_ci u32 dma_ctrl; 4862306a36Sopenharmony_ci /* Physical address of the next fbd */ 4962306a36Sopenharmony_ci u32 next_fbd_address; 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic void set_dma_ctrl(struct fbd *fb_desc, u32 ctrl) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci fb_desc->dma_ctrl = ctrl; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistruct isi_dma_desc { 5862306a36Sopenharmony_ci struct list_head list; 5962306a36Sopenharmony_ci struct fbd *p_fbd; 6062306a36Sopenharmony_ci dma_addr_t fbd_phys; 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* Frame buffer data */ 6462306a36Sopenharmony_cistruct frame_buffer { 6562306a36Sopenharmony_ci struct vb2_v4l2_buffer vb; 6662306a36Sopenharmony_ci struct isi_dma_desc *p_dma_desc; 6762306a36Sopenharmony_ci struct list_head list; 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistruct isi_graph_entity { 7162306a36Sopenharmony_ci struct device_node *node; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci struct v4l2_subdev *subdev; 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* 7762306a36Sopenharmony_ci * struct isi_format - ISI media bus format information 7862306a36Sopenharmony_ci * @fourcc: Fourcc code for this format 7962306a36Sopenharmony_ci * @mbus_code: V4L2 media bus format code. 8062306a36Sopenharmony_ci * @bpp: Bytes per pixel (when stored in memory) 8162306a36Sopenharmony_ci * @swap: Byte swap configuration value 8262306a36Sopenharmony_ci * @support: Indicates format supported by subdev 8362306a36Sopenharmony_ci * @skip: Skip duplicate format supported by subdev 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_cistruct isi_format { 8662306a36Sopenharmony_ci u32 fourcc; 8762306a36Sopenharmony_ci u32 mbus_code; 8862306a36Sopenharmony_ci u8 bpp; 8962306a36Sopenharmony_ci u32 swap; 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistruct atmel_isi { 9462306a36Sopenharmony_ci /* Protects the access of variables shared with the ISR */ 9562306a36Sopenharmony_ci spinlock_t irqlock; 9662306a36Sopenharmony_ci struct device *dev; 9762306a36Sopenharmony_ci void __iomem *regs; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci int sequence; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* Allocate descriptors for dma buffer use */ 10262306a36Sopenharmony_ci struct fbd *p_fb_descriptors; 10362306a36Sopenharmony_ci dma_addr_t fb_descriptors_phys; 10462306a36Sopenharmony_ci struct list_head dma_desc_head; 10562306a36Sopenharmony_ci struct isi_dma_desc dma_desc[VIDEO_MAX_FRAME]; 10662306a36Sopenharmony_ci bool enable_preview_path; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci struct completion complete; 10962306a36Sopenharmony_ci /* ISI peripheral clock */ 11062306a36Sopenharmony_ci struct clk *pclk; 11162306a36Sopenharmony_ci unsigned int irq; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci struct isi_platform_data pdata; 11462306a36Sopenharmony_ci u16 width_flags; /* max 12 bits */ 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci struct list_head video_buffer_list; 11762306a36Sopenharmony_ci struct frame_buffer *active; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci struct v4l2_device v4l2_dev; 12062306a36Sopenharmony_ci struct video_device *vdev; 12162306a36Sopenharmony_ci struct v4l2_async_notifier notifier; 12262306a36Sopenharmony_ci struct isi_graph_entity entity; 12362306a36Sopenharmony_ci struct v4l2_format fmt; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci const struct isi_format **user_formats; 12662306a36Sopenharmony_ci unsigned int num_user_formats; 12762306a36Sopenharmony_ci const struct isi_format *current_fmt; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci struct mutex lock; 13062306a36Sopenharmony_ci struct vb2_queue queue; 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci#define notifier_to_isi(n) container_of(n, struct atmel_isi, notifier) 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic void isi_writel(struct atmel_isi *isi, u32 reg, u32 val) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci writel(val, isi->regs + reg); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_cistatic u32 isi_readl(struct atmel_isi *isi, u32 reg) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci return readl(isi->regs + reg); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic void configure_geometry(struct atmel_isi *isi) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci u32 cfg2, psize; 14762306a36Sopenharmony_ci u32 fourcc = isi->current_fmt->fourcc; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci isi->enable_preview_path = fourcc == V4L2_PIX_FMT_RGB565 || 15062306a36Sopenharmony_ci fourcc == V4L2_PIX_FMT_RGB32 || 15162306a36Sopenharmony_ci fourcc == V4L2_PIX_FMT_Y16; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* According to sensor's output format to set cfg2 */ 15462306a36Sopenharmony_ci cfg2 = isi->current_fmt->swap; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); 15762306a36Sopenharmony_ci /* Set width */ 15862306a36Sopenharmony_ci cfg2 |= ((isi->fmt.fmt.pix.width - 1) << ISI_CFG2_IM_HSIZE_OFFSET) & 15962306a36Sopenharmony_ci ISI_CFG2_IM_HSIZE_MASK; 16062306a36Sopenharmony_ci /* Set height */ 16162306a36Sopenharmony_ci cfg2 |= ((isi->fmt.fmt.pix.height - 1) << ISI_CFG2_IM_VSIZE_OFFSET) 16262306a36Sopenharmony_ci & ISI_CFG2_IM_VSIZE_MASK; 16362306a36Sopenharmony_ci isi_writel(isi, ISI_CFG2, cfg2); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* No down sampling, preview size equal to sensor output size */ 16662306a36Sopenharmony_ci psize = ((isi->fmt.fmt.pix.width - 1) << ISI_PSIZE_PREV_HSIZE_OFFSET) & 16762306a36Sopenharmony_ci ISI_PSIZE_PREV_HSIZE_MASK; 16862306a36Sopenharmony_ci psize |= ((isi->fmt.fmt.pix.height - 1) << ISI_PSIZE_PREV_VSIZE_OFFSET) & 16962306a36Sopenharmony_ci ISI_PSIZE_PREV_VSIZE_MASK; 17062306a36Sopenharmony_ci isi_writel(isi, ISI_PSIZE, psize); 17162306a36Sopenharmony_ci isi_writel(isi, ISI_PDECF, ISI_PDECF_NO_SAMPLING); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic irqreturn_t atmel_isi_handle_streaming(struct atmel_isi *isi) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci if (isi->active) { 17762306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = &isi->active->vb; 17862306a36Sopenharmony_ci struct frame_buffer *buf = isi->active; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci list_del_init(&buf->list); 18162306a36Sopenharmony_ci vbuf->vb2_buf.timestamp = ktime_get_ns(); 18262306a36Sopenharmony_ci vbuf->sequence = isi->sequence++; 18362306a36Sopenharmony_ci vbuf->field = V4L2_FIELD_NONE; 18462306a36Sopenharmony_ci vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE); 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (list_empty(&isi->video_buffer_list)) { 18862306a36Sopenharmony_ci isi->active = NULL; 18962306a36Sopenharmony_ci } else { 19062306a36Sopenharmony_ci /* start next dma frame. */ 19162306a36Sopenharmony_ci isi->active = list_entry(isi->video_buffer_list.next, 19262306a36Sopenharmony_ci struct frame_buffer, list); 19362306a36Sopenharmony_ci if (!isi->enable_preview_path) { 19462306a36Sopenharmony_ci isi_writel(isi, ISI_DMA_C_DSCR, 19562306a36Sopenharmony_ci (u32)isi->active->p_dma_desc->fbd_phys); 19662306a36Sopenharmony_ci isi_writel(isi, ISI_DMA_C_CTRL, 19762306a36Sopenharmony_ci ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE); 19862306a36Sopenharmony_ci isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH); 19962306a36Sopenharmony_ci } else { 20062306a36Sopenharmony_ci isi_writel(isi, ISI_DMA_P_DSCR, 20162306a36Sopenharmony_ci (u32)isi->active->p_dma_desc->fbd_phys); 20262306a36Sopenharmony_ci isi_writel(isi, ISI_DMA_P_CTRL, 20362306a36Sopenharmony_ci ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE); 20462306a36Sopenharmony_ci isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_P_CH); 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci return IRQ_HANDLED; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci/* ISI interrupt service routine */ 21162306a36Sopenharmony_cistatic irqreturn_t isi_interrupt(int irq, void *dev_id) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct atmel_isi *isi = dev_id; 21462306a36Sopenharmony_ci u32 status, mask, pending; 21562306a36Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci spin_lock(&isi->irqlock); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci status = isi_readl(isi, ISI_STATUS); 22062306a36Sopenharmony_ci mask = isi_readl(isi, ISI_INTMASK); 22162306a36Sopenharmony_ci pending = status & mask; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (pending & ISI_CTRL_SRST) { 22462306a36Sopenharmony_ci complete(&isi->complete); 22562306a36Sopenharmony_ci isi_writel(isi, ISI_INTDIS, ISI_CTRL_SRST); 22662306a36Sopenharmony_ci ret = IRQ_HANDLED; 22762306a36Sopenharmony_ci } else if (pending & ISI_CTRL_DIS) { 22862306a36Sopenharmony_ci complete(&isi->complete); 22962306a36Sopenharmony_ci isi_writel(isi, ISI_INTDIS, ISI_CTRL_DIS); 23062306a36Sopenharmony_ci ret = IRQ_HANDLED; 23162306a36Sopenharmony_ci } else { 23262306a36Sopenharmony_ci if (likely(pending & ISI_SR_CXFR_DONE) || 23362306a36Sopenharmony_ci likely(pending & ISI_SR_PXFR_DONE)) 23462306a36Sopenharmony_ci ret = atmel_isi_handle_streaming(isi); 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci spin_unlock(&isi->irqlock); 23862306a36Sopenharmony_ci return ret; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci#define WAIT_ISI_RESET 1 24262306a36Sopenharmony_ci#define WAIT_ISI_DISABLE 0 24362306a36Sopenharmony_cistatic int atmel_isi_wait_status(struct atmel_isi *isi, int wait_reset) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci unsigned long timeout; 24662306a36Sopenharmony_ci /* 24762306a36Sopenharmony_ci * The reset or disable will only succeed if we have a 24862306a36Sopenharmony_ci * pixel clock from the camera. 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_ci init_completion(&isi->complete); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (wait_reset) { 25362306a36Sopenharmony_ci isi_writel(isi, ISI_INTEN, ISI_CTRL_SRST); 25462306a36Sopenharmony_ci isi_writel(isi, ISI_CTRL, ISI_CTRL_SRST); 25562306a36Sopenharmony_ci } else { 25662306a36Sopenharmony_ci isi_writel(isi, ISI_INTEN, ISI_CTRL_DIS); 25762306a36Sopenharmony_ci isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci timeout = wait_for_completion_timeout(&isi->complete, 26162306a36Sopenharmony_ci msecs_to_jiffies(500)); 26262306a36Sopenharmony_ci if (timeout == 0) 26362306a36Sopenharmony_ci return -ETIMEDOUT; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return 0; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/* ------------------------------------------------------------------ 26962306a36Sopenharmony_ci Videobuf operations 27062306a36Sopenharmony_ci ------------------------------------------------------------------*/ 27162306a36Sopenharmony_cistatic int queue_setup(struct vb2_queue *vq, 27262306a36Sopenharmony_ci unsigned int *nbuffers, unsigned int *nplanes, 27362306a36Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct atmel_isi *isi = vb2_get_drv_priv(vq); 27662306a36Sopenharmony_ci unsigned long size; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci size = isi->fmt.fmt.pix.sizeimage; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* Make sure the image size is large enough. */ 28162306a36Sopenharmony_ci if (*nplanes) 28262306a36Sopenharmony_ci return sizes[0] < size ? -EINVAL : 0; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci *nplanes = 1; 28562306a36Sopenharmony_ci sizes[0] = size; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci isi->active = NULL; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci dev_dbg(isi->dev, "%s, count=%d, size=%ld\n", __func__, 29062306a36Sopenharmony_ci *nbuffers, size); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci return 0; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic int buffer_init(struct vb2_buffer *vb) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 29862306a36Sopenharmony_ci struct frame_buffer *buf = container_of(vbuf, struct frame_buffer, vb); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci buf->p_dma_desc = NULL; 30162306a36Sopenharmony_ci INIT_LIST_HEAD(&buf->list); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return 0; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic int buffer_prepare(struct vb2_buffer *vb) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 30962306a36Sopenharmony_ci struct frame_buffer *buf = container_of(vbuf, struct frame_buffer, vb); 31062306a36Sopenharmony_ci struct atmel_isi *isi = vb2_get_drv_priv(vb->vb2_queue); 31162306a36Sopenharmony_ci unsigned long size; 31262306a36Sopenharmony_ci struct isi_dma_desc *desc; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci size = isi->fmt.fmt.pix.sizeimage; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (vb2_plane_size(vb, 0) < size) { 31762306a36Sopenharmony_ci dev_err(isi->dev, "%s data will not fit into plane (%lu < %lu)\n", 31862306a36Sopenharmony_ci __func__, vb2_plane_size(vb, 0), size); 31962306a36Sopenharmony_ci return -EINVAL; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci vb2_set_plane_payload(vb, 0, size); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (!buf->p_dma_desc) { 32562306a36Sopenharmony_ci if (list_empty(&isi->dma_desc_head)) { 32662306a36Sopenharmony_ci dev_err(isi->dev, "Not enough dma descriptors.\n"); 32762306a36Sopenharmony_ci return -EINVAL; 32862306a36Sopenharmony_ci } else { 32962306a36Sopenharmony_ci /* Get an available descriptor */ 33062306a36Sopenharmony_ci desc = list_entry(isi->dma_desc_head.next, 33162306a36Sopenharmony_ci struct isi_dma_desc, list); 33262306a36Sopenharmony_ci /* Delete the descriptor since now it is used */ 33362306a36Sopenharmony_ci list_del_init(&desc->list); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* Initialize the dma descriptor */ 33662306a36Sopenharmony_ci desc->p_fbd->fb_address = 33762306a36Sopenharmony_ci vb2_dma_contig_plane_dma_addr(vb, 0); 33862306a36Sopenharmony_ci desc->p_fbd->next_fbd_address = 0; 33962306a36Sopenharmony_ci set_dma_ctrl(desc->p_fbd, ISI_DMA_CTRL_WB); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci buf->p_dma_desc = desc; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci return 0; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic void buffer_cleanup(struct vb2_buffer *vb) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 35062306a36Sopenharmony_ci struct atmel_isi *isi = vb2_get_drv_priv(vb->vb2_queue); 35162306a36Sopenharmony_ci struct frame_buffer *buf = container_of(vbuf, struct frame_buffer, vb); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* This descriptor is available now and we add to head list */ 35462306a36Sopenharmony_ci if (buf->p_dma_desc) 35562306a36Sopenharmony_ci list_add(&buf->p_dma_desc->list, &isi->dma_desc_head); 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci u32 ctrl, cfg1; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci cfg1 = isi_readl(isi, ISI_CFG1); 36362306a36Sopenharmony_ci /* Enable irq: cxfr for the codec path, pxfr for the preview path */ 36462306a36Sopenharmony_ci isi_writel(isi, ISI_INTEN, 36562306a36Sopenharmony_ci ISI_SR_CXFR_DONE | ISI_SR_PXFR_DONE); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* Check if already in a frame */ 36862306a36Sopenharmony_ci if (!isi->enable_preview_path) { 36962306a36Sopenharmony_ci if (isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) { 37062306a36Sopenharmony_ci dev_err(isi->dev, "Already in frame handling.\n"); 37162306a36Sopenharmony_ci return; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci isi_writel(isi, ISI_DMA_C_DSCR, 37562306a36Sopenharmony_ci (u32)buffer->p_dma_desc->fbd_phys); 37662306a36Sopenharmony_ci isi_writel(isi, ISI_DMA_C_CTRL, 37762306a36Sopenharmony_ci ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE); 37862306a36Sopenharmony_ci isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH); 37962306a36Sopenharmony_ci } else { 38062306a36Sopenharmony_ci isi_writel(isi, ISI_DMA_P_DSCR, 38162306a36Sopenharmony_ci (u32)buffer->p_dma_desc->fbd_phys); 38262306a36Sopenharmony_ci isi_writel(isi, ISI_DMA_P_CTRL, 38362306a36Sopenharmony_ci ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE); 38462306a36Sopenharmony_ci isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_P_CH); 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci cfg1 &= ~ISI_CFG1_FRATE_DIV_MASK; 38862306a36Sopenharmony_ci /* Enable linked list */ 38962306a36Sopenharmony_ci cfg1 |= isi->pdata.frate | ISI_CFG1_DISCR; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* Enable ISI */ 39262306a36Sopenharmony_ci ctrl = ISI_CTRL_EN; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (!isi->enable_preview_path) 39562306a36Sopenharmony_ci ctrl |= ISI_CTRL_CDC; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci isi_writel(isi, ISI_CTRL, ctrl); 39862306a36Sopenharmony_ci isi_writel(isi, ISI_CFG1, cfg1); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic void buffer_queue(struct vb2_buffer *vb) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 40462306a36Sopenharmony_ci struct atmel_isi *isi = vb2_get_drv_priv(vb->vb2_queue); 40562306a36Sopenharmony_ci struct frame_buffer *buf = container_of(vbuf, struct frame_buffer, vb); 40662306a36Sopenharmony_ci unsigned long flags = 0; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci spin_lock_irqsave(&isi->irqlock, flags); 40962306a36Sopenharmony_ci list_add_tail(&buf->list, &isi->video_buffer_list); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (!isi->active) { 41262306a36Sopenharmony_ci isi->active = buf; 41362306a36Sopenharmony_ci if (vb2_is_streaming(vb->vb2_queue)) 41462306a36Sopenharmony_ci start_dma(isi, buf); 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci spin_unlock_irqrestore(&isi->irqlock, flags); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic int start_streaming(struct vb2_queue *vq, unsigned int count) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci struct atmel_isi *isi = vb2_get_drv_priv(vq); 42262306a36Sopenharmony_ci struct frame_buffer *buf, *node; 42362306a36Sopenharmony_ci int ret; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(isi->dev); 42662306a36Sopenharmony_ci if (ret < 0) 42762306a36Sopenharmony_ci return ret; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* Enable stream on the sub device */ 43062306a36Sopenharmony_ci ret = v4l2_subdev_call(isi->entity.subdev, video, s_stream, 1); 43162306a36Sopenharmony_ci if (ret && ret != -ENOIOCTLCMD) { 43262306a36Sopenharmony_ci dev_err(isi->dev, "stream on failed in subdev\n"); 43362306a36Sopenharmony_ci goto err_start_stream; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* Reset ISI */ 43762306a36Sopenharmony_ci ret = atmel_isi_wait_status(isi, WAIT_ISI_RESET); 43862306a36Sopenharmony_ci if (ret < 0) { 43962306a36Sopenharmony_ci dev_err(isi->dev, "Reset ISI timed out\n"); 44062306a36Sopenharmony_ci goto err_reset; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci /* Disable all interrupts */ 44362306a36Sopenharmony_ci isi_writel(isi, ISI_INTDIS, (u32)~0UL); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci isi->sequence = 0; 44662306a36Sopenharmony_ci configure_geometry(isi); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci spin_lock_irq(&isi->irqlock); 44962306a36Sopenharmony_ci /* Clear any pending interrupt */ 45062306a36Sopenharmony_ci isi_readl(isi, ISI_STATUS); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci start_dma(isi, isi->active); 45362306a36Sopenharmony_ci spin_unlock_irq(&isi->irqlock); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci return 0; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cierr_reset: 45862306a36Sopenharmony_ci v4l2_subdev_call(isi->entity.subdev, video, s_stream, 0); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cierr_start_stream: 46162306a36Sopenharmony_ci pm_runtime_put(isi->dev); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci spin_lock_irq(&isi->irqlock); 46462306a36Sopenharmony_ci isi->active = NULL; 46562306a36Sopenharmony_ci /* Release all active buffers */ 46662306a36Sopenharmony_ci list_for_each_entry_safe(buf, node, &isi->video_buffer_list, list) { 46762306a36Sopenharmony_ci list_del_init(&buf->list); 46862306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci spin_unlock_irq(&isi->irqlock); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci return ret; 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci/* abort streaming and wait for last buffer */ 47662306a36Sopenharmony_cistatic void stop_streaming(struct vb2_queue *vq) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci struct atmel_isi *isi = vb2_get_drv_priv(vq); 47962306a36Sopenharmony_ci struct frame_buffer *buf, *node; 48062306a36Sopenharmony_ci int ret = 0; 48162306a36Sopenharmony_ci unsigned long timeout; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* Disable stream on the sub device */ 48462306a36Sopenharmony_ci ret = v4l2_subdev_call(isi->entity.subdev, video, s_stream, 0); 48562306a36Sopenharmony_ci if (ret && ret != -ENOIOCTLCMD) 48662306a36Sopenharmony_ci dev_err(isi->dev, "stream off failed in subdev\n"); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci spin_lock_irq(&isi->irqlock); 48962306a36Sopenharmony_ci isi->active = NULL; 49062306a36Sopenharmony_ci /* Release all active buffers */ 49162306a36Sopenharmony_ci list_for_each_entry_safe(buf, node, &isi->video_buffer_list, list) { 49262306a36Sopenharmony_ci list_del_init(&buf->list); 49362306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci spin_unlock_irq(&isi->irqlock); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if (!isi->enable_preview_path) { 49862306a36Sopenharmony_ci timeout = jiffies + (FRAME_INTERVAL_MILLI_SEC * HZ) / 1000; 49962306a36Sopenharmony_ci /* Wait until the end of the current frame. */ 50062306a36Sopenharmony_ci while ((isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) && 50162306a36Sopenharmony_ci time_before(jiffies, timeout)) 50262306a36Sopenharmony_ci msleep(1); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (time_after(jiffies, timeout)) 50562306a36Sopenharmony_ci dev_err(isi->dev, 50662306a36Sopenharmony_ci "Timeout waiting for finishing codec request\n"); 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* Disable interrupts */ 51062306a36Sopenharmony_ci isi_writel(isi, ISI_INTDIS, 51162306a36Sopenharmony_ci ISI_SR_CXFR_DONE | ISI_SR_PXFR_DONE); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci /* Disable ISI and wait for it is done */ 51462306a36Sopenharmony_ci ret = atmel_isi_wait_status(isi, WAIT_ISI_DISABLE); 51562306a36Sopenharmony_ci if (ret < 0) 51662306a36Sopenharmony_ci dev_err(isi->dev, "Disable ISI timed out\n"); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci pm_runtime_put(isi->dev); 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cistatic const struct vb2_ops isi_video_qops = { 52262306a36Sopenharmony_ci .queue_setup = queue_setup, 52362306a36Sopenharmony_ci .buf_init = buffer_init, 52462306a36Sopenharmony_ci .buf_prepare = buffer_prepare, 52562306a36Sopenharmony_ci .buf_cleanup = buffer_cleanup, 52662306a36Sopenharmony_ci .buf_queue = buffer_queue, 52762306a36Sopenharmony_ci .start_streaming = start_streaming, 52862306a36Sopenharmony_ci .stop_streaming = stop_streaming, 52962306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 53062306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 53162306a36Sopenharmony_ci}; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic int isi_g_fmt_vid_cap(struct file *file, void *priv, 53462306a36Sopenharmony_ci struct v4l2_format *fmt) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci struct atmel_isi *isi = video_drvdata(file); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci *fmt = isi->fmt; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci return 0; 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cistatic const struct isi_format *find_format_by_fourcc(struct atmel_isi *isi, 54462306a36Sopenharmony_ci unsigned int fourcc) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci unsigned int num_formats = isi->num_user_formats; 54762306a36Sopenharmony_ci const struct isi_format *fmt; 54862306a36Sopenharmony_ci unsigned int i; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci for (i = 0; i < num_formats; i++) { 55162306a36Sopenharmony_ci fmt = isi->user_formats[i]; 55262306a36Sopenharmony_ci if (fmt->fourcc == fourcc) 55362306a36Sopenharmony_ci return fmt; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci return NULL; 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic void isi_try_fse(struct atmel_isi *isi, const struct isi_format *isi_fmt, 56062306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci int ret; 56362306a36Sopenharmony_ci struct v4l2_subdev_frame_size_enum fse = { 56462306a36Sopenharmony_ci .code = isi_fmt->mbus_code, 56562306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_TRY, 56662306a36Sopenharmony_ci }; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci ret = v4l2_subdev_call(isi->entity.subdev, pad, enum_frame_size, 56962306a36Sopenharmony_ci sd_state, &fse); 57062306a36Sopenharmony_ci /* 57162306a36Sopenharmony_ci * Attempt to obtain format size from subdev. If not available, 57262306a36Sopenharmony_ci * just use the maximum ISI can receive. 57362306a36Sopenharmony_ci */ 57462306a36Sopenharmony_ci if (ret) { 57562306a36Sopenharmony_ci sd_state->pads->try_crop.width = MAX_SUPPORT_WIDTH; 57662306a36Sopenharmony_ci sd_state->pads->try_crop.height = MAX_SUPPORT_HEIGHT; 57762306a36Sopenharmony_ci } else { 57862306a36Sopenharmony_ci sd_state->pads->try_crop.width = fse.max_width; 57962306a36Sopenharmony_ci sd_state->pads->try_crop.height = fse.max_height; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_cistatic int isi_try_fmt(struct atmel_isi *isi, struct v4l2_format *f, 58462306a36Sopenharmony_ci const struct isi_format **current_fmt) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci const struct isi_format *isi_fmt; 58762306a36Sopenharmony_ci struct v4l2_pix_format *pixfmt = &f->fmt.pix; 58862306a36Sopenharmony_ci struct v4l2_subdev_pad_config pad_cfg = {}; 58962306a36Sopenharmony_ci struct v4l2_subdev_state pad_state = { 59062306a36Sopenharmony_ci .pads = &pad_cfg, 59162306a36Sopenharmony_ci }; 59262306a36Sopenharmony_ci struct v4l2_subdev_format format = { 59362306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_TRY, 59462306a36Sopenharmony_ci }; 59562306a36Sopenharmony_ci int ret; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci isi_fmt = find_format_by_fourcc(isi, pixfmt->pixelformat); 59862306a36Sopenharmony_ci if (!isi_fmt) { 59962306a36Sopenharmony_ci isi_fmt = isi->user_formats[isi->num_user_formats - 1]; 60062306a36Sopenharmony_ci pixfmt->pixelformat = isi_fmt->fourcc; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* Limit to Atmel ISI hardware capabilities */ 60462306a36Sopenharmony_ci pixfmt->width = clamp(pixfmt->width, 0U, MAX_SUPPORT_WIDTH); 60562306a36Sopenharmony_ci pixfmt->height = clamp(pixfmt->height, 0U, MAX_SUPPORT_HEIGHT); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci v4l2_fill_mbus_format(&format.format, pixfmt, isi_fmt->mbus_code); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci isi_try_fse(isi, isi_fmt, &pad_state); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci ret = v4l2_subdev_call(isi->entity.subdev, pad, set_fmt, 61262306a36Sopenharmony_ci &pad_state, &format); 61362306a36Sopenharmony_ci if (ret < 0) 61462306a36Sopenharmony_ci return ret; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci v4l2_fill_pix_format(pixfmt, &format.format); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci pixfmt->field = V4L2_FIELD_NONE; 61962306a36Sopenharmony_ci pixfmt->bytesperline = pixfmt->width * isi_fmt->bpp; 62062306a36Sopenharmony_ci pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (current_fmt) 62362306a36Sopenharmony_ci *current_fmt = isi_fmt; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci return 0; 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic int isi_set_fmt(struct atmel_isi *isi, struct v4l2_format *f) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci struct v4l2_subdev_format format = { 63162306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 63262306a36Sopenharmony_ci }; 63362306a36Sopenharmony_ci const struct isi_format *current_fmt; 63462306a36Sopenharmony_ci int ret; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci ret = isi_try_fmt(isi, f, ¤t_fmt); 63762306a36Sopenharmony_ci if (ret) 63862306a36Sopenharmony_ci return ret; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci v4l2_fill_mbus_format(&format.format, &f->fmt.pix, 64162306a36Sopenharmony_ci current_fmt->mbus_code); 64262306a36Sopenharmony_ci ret = v4l2_subdev_call(isi->entity.subdev, pad, 64362306a36Sopenharmony_ci set_fmt, NULL, &format); 64462306a36Sopenharmony_ci if (ret < 0) 64562306a36Sopenharmony_ci return ret; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci isi->fmt = *f; 64862306a36Sopenharmony_ci isi->current_fmt = current_fmt; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci return 0; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic int isi_s_fmt_vid_cap(struct file *file, void *priv, 65462306a36Sopenharmony_ci struct v4l2_format *f) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci struct atmel_isi *isi = video_drvdata(file); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci if (vb2_is_streaming(&isi->queue)) 65962306a36Sopenharmony_ci return -EBUSY; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci return isi_set_fmt(isi, f); 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_cistatic int isi_try_fmt_vid_cap(struct file *file, void *priv, 66562306a36Sopenharmony_ci struct v4l2_format *f) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci struct atmel_isi *isi = video_drvdata(file); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci return isi_try_fmt(isi, f, NULL); 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cistatic int isi_enum_fmt_vid_cap(struct file *file, void *priv, 67362306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci struct atmel_isi *isi = video_drvdata(file); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci if (f->index >= isi->num_user_formats) 67862306a36Sopenharmony_ci return -EINVAL; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci f->pixelformat = isi->user_formats[f->index]->fourcc; 68162306a36Sopenharmony_ci return 0; 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic int isi_querycap(struct file *file, void *priv, 68562306a36Sopenharmony_ci struct v4l2_capability *cap) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci strscpy(cap->driver, "atmel-isi", sizeof(cap->driver)); 68862306a36Sopenharmony_ci strscpy(cap->card, "Atmel Image Sensor Interface", sizeof(cap->card)); 68962306a36Sopenharmony_ci strscpy(cap->bus_info, "platform:isi", sizeof(cap->bus_info)); 69062306a36Sopenharmony_ci return 0; 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_cistatic int isi_enum_input(struct file *file, void *priv, 69462306a36Sopenharmony_ci struct v4l2_input *i) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci if (i->index != 0) 69762306a36Sopenharmony_ci return -EINVAL; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci i->type = V4L2_INPUT_TYPE_CAMERA; 70062306a36Sopenharmony_ci strscpy(i->name, "Camera", sizeof(i->name)); 70162306a36Sopenharmony_ci return 0; 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic int isi_g_input(struct file *file, void *priv, unsigned int *i) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci *i = 0; 70762306a36Sopenharmony_ci return 0; 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cistatic int isi_s_input(struct file *file, void *priv, unsigned int i) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci if (i > 0) 71362306a36Sopenharmony_ci return -EINVAL; 71462306a36Sopenharmony_ci return 0; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic int isi_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci struct atmel_isi *isi = video_drvdata(file); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci return v4l2_g_parm_cap(video_devdata(file), isi->entity.subdev, a); 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cistatic int isi_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) 72562306a36Sopenharmony_ci{ 72662306a36Sopenharmony_ci struct atmel_isi *isi = video_drvdata(file); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci return v4l2_s_parm_cap(video_devdata(file), isi->entity.subdev, a); 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic int isi_enum_framesizes(struct file *file, void *fh, 73262306a36Sopenharmony_ci struct v4l2_frmsizeenum *fsize) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci struct atmel_isi *isi = video_drvdata(file); 73562306a36Sopenharmony_ci const struct isi_format *isi_fmt; 73662306a36Sopenharmony_ci struct v4l2_subdev_frame_size_enum fse = { 73762306a36Sopenharmony_ci .index = fsize->index, 73862306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 73962306a36Sopenharmony_ci }; 74062306a36Sopenharmony_ci int ret; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci isi_fmt = find_format_by_fourcc(isi, fsize->pixel_format); 74362306a36Sopenharmony_ci if (!isi_fmt) 74462306a36Sopenharmony_ci return -EINVAL; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci fse.code = isi_fmt->mbus_code; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci ret = v4l2_subdev_call(isi->entity.subdev, pad, enum_frame_size, 74962306a36Sopenharmony_ci NULL, &fse); 75062306a36Sopenharmony_ci if (ret) 75162306a36Sopenharmony_ci return ret; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; 75462306a36Sopenharmony_ci fsize->discrete.width = fse.max_width; 75562306a36Sopenharmony_ci fsize->discrete.height = fse.max_height; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci return 0; 75862306a36Sopenharmony_ci} 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_cistatic int isi_enum_frameintervals(struct file *file, void *fh, 76162306a36Sopenharmony_ci struct v4l2_frmivalenum *fival) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci struct atmel_isi *isi = video_drvdata(file); 76462306a36Sopenharmony_ci const struct isi_format *isi_fmt; 76562306a36Sopenharmony_ci struct v4l2_subdev_frame_interval_enum fie = { 76662306a36Sopenharmony_ci .index = fival->index, 76762306a36Sopenharmony_ci .width = fival->width, 76862306a36Sopenharmony_ci .height = fival->height, 76962306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 77062306a36Sopenharmony_ci }; 77162306a36Sopenharmony_ci int ret; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci isi_fmt = find_format_by_fourcc(isi, fival->pixel_format); 77462306a36Sopenharmony_ci if (!isi_fmt) 77562306a36Sopenharmony_ci return -EINVAL; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci fie.code = isi_fmt->mbus_code; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci ret = v4l2_subdev_call(isi->entity.subdev, pad, 78062306a36Sopenharmony_ci enum_frame_interval, NULL, &fie); 78162306a36Sopenharmony_ci if (ret) 78262306a36Sopenharmony_ci return ret; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; 78562306a36Sopenharmony_ci fival->discrete = fie.interval; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci return 0; 78862306a36Sopenharmony_ci} 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_cistatic int isi_camera_set_bus_param(struct atmel_isi *isi) 79162306a36Sopenharmony_ci{ 79262306a36Sopenharmony_ci u32 cfg1 = 0; 79362306a36Sopenharmony_ci int ret; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci /* set bus param for ISI */ 79662306a36Sopenharmony_ci if (isi->pdata.hsync_act_low) 79762306a36Sopenharmony_ci cfg1 |= ISI_CFG1_HSYNC_POL_ACTIVE_LOW; 79862306a36Sopenharmony_ci if (isi->pdata.vsync_act_low) 79962306a36Sopenharmony_ci cfg1 |= ISI_CFG1_VSYNC_POL_ACTIVE_LOW; 80062306a36Sopenharmony_ci if (isi->pdata.pclk_act_falling) 80162306a36Sopenharmony_ci cfg1 |= ISI_CFG1_PIXCLK_POL_ACTIVE_FALLING; 80262306a36Sopenharmony_ci if (isi->pdata.has_emb_sync) 80362306a36Sopenharmony_ci cfg1 |= ISI_CFG1_EMB_SYNC; 80462306a36Sopenharmony_ci if (isi->pdata.full_mode) 80562306a36Sopenharmony_ci cfg1 |= ISI_CFG1_FULL_MODE; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci cfg1 |= ISI_CFG1_THMASK_BEATS_16; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci /* Enable PM and peripheral clock before operate isi registers */ 81062306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(isi->dev); 81162306a36Sopenharmony_ci if (ret < 0) 81262306a36Sopenharmony_ci return ret; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); 81562306a36Sopenharmony_ci isi_writel(isi, ISI_CFG1, cfg1); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci pm_runtime_put(isi->dev); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci return 0; 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci/* -----------------------------------------------------------------------*/ 82362306a36Sopenharmony_cistatic int atmel_isi_parse_dt(struct atmel_isi *isi, 82462306a36Sopenharmony_ci struct platform_device *pdev) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 82762306a36Sopenharmony_ci struct v4l2_fwnode_endpoint ep = { .bus_type = 0 }; 82862306a36Sopenharmony_ci int err; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci /* Default settings for ISI */ 83162306a36Sopenharmony_ci isi->pdata.full_mode = 1; 83262306a36Sopenharmony_ci isi->pdata.frate = ISI_CFG1_FRATE_CAPTURE_ALL; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci np = of_graph_get_next_endpoint(np, NULL); 83562306a36Sopenharmony_ci if (!np) { 83662306a36Sopenharmony_ci dev_err(&pdev->dev, "Could not find the endpoint\n"); 83762306a36Sopenharmony_ci return -EINVAL; 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci err = v4l2_fwnode_endpoint_parse(of_fwnode_handle(np), &ep); 84162306a36Sopenharmony_ci of_node_put(np); 84262306a36Sopenharmony_ci if (err) { 84362306a36Sopenharmony_ci dev_err(&pdev->dev, "Could not parse the endpoint\n"); 84462306a36Sopenharmony_ci return err; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci switch (ep.bus.parallel.bus_width) { 84862306a36Sopenharmony_ci case 8: 84962306a36Sopenharmony_ci isi->pdata.data_width_flags = ISI_DATAWIDTH_8; 85062306a36Sopenharmony_ci break; 85162306a36Sopenharmony_ci case 10: 85262306a36Sopenharmony_ci isi->pdata.data_width_flags = 85362306a36Sopenharmony_ci ISI_DATAWIDTH_8 | ISI_DATAWIDTH_10; 85462306a36Sopenharmony_ci break; 85562306a36Sopenharmony_ci default: 85662306a36Sopenharmony_ci dev_err(&pdev->dev, "Unsupported bus width: %d\n", 85762306a36Sopenharmony_ci ep.bus.parallel.bus_width); 85862306a36Sopenharmony_ci return -EINVAL; 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci if (ep.bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) 86262306a36Sopenharmony_ci isi->pdata.hsync_act_low = true; 86362306a36Sopenharmony_ci if (ep.bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) 86462306a36Sopenharmony_ci isi->pdata.vsync_act_low = true; 86562306a36Sopenharmony_ci if (ep.bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) 86662306a36Sopenharmony_ci isi->pdata.pclk_act_falling = true; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci if (ep.bus_type == V4L2_MBUS_BT656) 86962306a36Sopenharmony_ci isi->pdata.has_emb_sync = true; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci return 0; 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cistatic int isi_open(struct file *file) 87562306a36Sopenharmony_ci{ 87662306a36Sopenharmony_ci struct atmel_isi *isi = video_drvdata(file); 87762306a36Sopenharmony_ci struct v4l2_subdev *sd = isi->entity.subdev; 87862306a36Sopenharmony_ci int ret; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci if (mutex_lock_interruptible(&isi->lock)) 88162306a36Sopenharmony_ci return -ERESTARTSYS; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci ret = v4l2_fh_open(file); 88462306a36Sopenharmony_ci if (ret < 0) 88562306a36Sopenharmony_ci goto unlock; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci if (!v4l2_fh_is_singular_file(file)) 88862306a36Sopenharmony_ci goto fh_rel; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci ret = v4l2_subdev_call(sd, core, s_power, 1); 89162306a36Sopenharmony_ci if (ret < 0 && ret != -ENOIOCTLCMD) 89262306a36Sopenharmony_ci goto fh_rel; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci ret = isi_set_fmt(isi, &isi->fmt); 89562306a36Sopenharmony_ci if (ret) 89662306a36Sopenharmony_ci v4l2_subdev_call(sd, core, s_power, 0); 89762306a36Sopenharmony_cifh_rel: 89862306a36Sopenharmony_ci if (ret) 89962306a36Sopenharmony_ci v4l2_fh_release(file); 90062306a36Sopenharmony_ciunlock: 90162306a36Sopenharmony_ci mutex_unlock(&isi->lock); 90262306a36Sopenharmony_ci return ret; 90362306a36Sopenharmony_ci} 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_cistatic int isi_release(struct file *file) 90662306a36Sopenharmony_ci{ 90762306a36Sopenharmony_ci struct atmel_isi *isi = video_drvdata(file); 90862306a36Sopenharmony_ci struct v4l2_subdev *sd = isi->entity.subdev; 90962306a36Sopenharmony_ci bool fh_singular; 91062306a36Sopenharmony_ci int ret; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci mutex_lock(&isi->lock); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci fh_singular = v4l2_fh_is_singular_file(file); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci ret = _vb2_fop_release(file, NULL); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (fh_singular) 91962306a36Sopenharmony_ci v4l2_subdev_call(sd, core, s_power, 0); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci mutex_unlock(&isi->lock); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci return ret; 92462306a36Sopenharmony_ci} 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops isi_ioctl_ops = { 92762306a36Sopenharmony_ci .vidioc_querycap = isi_querycap, 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap = isi_try_fmt_vid_cap, 93062306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap = isi_g_fmt_vid_cap, 93162306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap = isi_s_fmt_vid_cap, 93262306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = isi_enum_fmt_vid_cap, 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci .vidioc_enum_input = isi_enum_input, 93562306a36Sopenharmony_ci .vidioc_g_input = isi_g_input, 93662306a36Sopenharmony_ci .vidioc_s_input = isi_s_input, 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci .vidioc_g_parm = isi_g_parm, 93962306a36Sopenharmony_ci .vidioc_s_parm = isi_s_parm, 94062306a36Sopenharmony_ci .vidioc_enum_framesizes = isi_enum_framesizes, 94162306a36Sopenharmony_ci .vidioc_enum_frameintervals = isi_enum_frameintervals, 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 94462306a36Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 94562306a36Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 94662306a36Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 94762306a36Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 94862306a36Sopenharmony_ci .vidioc_expbuf = vb2_ioctl_expbuf, 94962306a36Sopenharmony_ci .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 95062306a36Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 95162306a36Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci .vidioc_log_status = v4l2_ctrl_log_status, 95462306a36Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 95562306a36Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 95662306a36Sopenharmony_ci}; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_cistatic const struct v4l2_file_operations isi_fops = { 95962306a36Sopenharmony_ci .owner = THIS_MODULE, 96062306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 96162306a36Sopenharmony_ci .open = isi_open, 96262306a36Sopenharmony_ci .release = isi_release, 96362306a36Sopenharmony_ci .poll = vb2_fop_poll, 96462306a36Sopenharmony_ci .mmap = vb2_fop_mmap, 96562306a36Sopenharmony_ci .read = vb2_fop_read, 96662306a36Sopenharmony_ci}; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_cistatic int isi_set_default_fmt(struct atmel_isi *isi) 96962306a36Sopenharmony_ci{ 97062306a36Sopenharmony_ci struct v4l2_format f = { 97162306a36Sopenharmony_ci .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, 97262306a36Sopenharmony_ci .fmt.pix = { 97362306a36Sopenharmony_ci .width = VGA_WIDTH, 97462306a36Sopenharmony_ci .height = VGA_HEIGHT, 97562306a36Sopenharmony_ci .field = V4L2_FIELD_NONE, 97662306a36Sopenharmony_ci .pixelformat = isi->user_formats[0]->fourcc, 97762306a36Sopenharmony_ci }, 97862306a36Sopenharmony_ci }; 97962306a36Sopenharmony_ci int ret; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci ret = isi_try_fmt(isi, &f, NULL); 98262306a36Sopenharmony_ci if (ret) 98362306a36Sopenharmony_ci return ret; 98462306a36Sopenharmony_ci isi->current_fmt = isi->user_formats[0]; 98562306a36Sopenharmony_ci isi->fmt = f; 98662306a36Sopenharmony_ci return 0; 98762306a36Sopenharmony_ci} 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_cistatic const struct isi_format isi_formats[] = { 99062306a36Sopenharmony_ci { 99162306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_YUYV, 99262306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, 99362306a36Sopenharmony_ci .bpp = 2, 99462306a36Sopenharmony_ci .swap = ISI_CFG2_YCC_SWAP_DEFAULT, 99562306a36Sopenharmony_ci }, { 99662306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_YUYV, 99762306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8, 99862306a36Sopenharmony_ci .bpp = 2, 99962306a36Sopenharmony_ci .swap = ISI_CFG2_YCC_SWAP_MODE_1, 100062306a36Sopenharmony_ci }, { 100162306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_YUYV, 100262306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, 100362306a36Sopenharmony_ci .bpp = 2, 100462306a36Sopenharmony_ci .swap = ISI_CFG2_YCC_SWAP_MODE_2, 100562306a36Sopenharmony_ci }, { 100662306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_YUYV, 100762306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8, 100862306a36Sopenharmony_ci .bpp = 2, 100962306a36Sopenharmony_ci .swap = ISI_CFG2_YCC_SWAP_MODE_3, 101062306a36Sopenharmony_ci }, { 101162306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB565, 101262306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, 101362306a36Sopenharmony_ci .bpp = 2, 101462306a36Sopenharmony_ci .swap = ISI_CFG2_YCC_SWAP_MODE_2, 101562306a36Sopenharmony_ci }, { 101662306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB565, 101762306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8, 101862306a36Sopenharmony_ci .bpp = 2, 101962306a36Sopenharmony_ci .swap = ISI_CFG2_YCC_SWAP_MODE_3, 102062306a36Sopenharmony_ci }, { 102162306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB565, 102262306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, 102362306a36Sopenharmony_ci .bpp = 2, 102462306a36Sopenharmony_ci .swap = ISI_CFG2_YCC_SWAP_DEFAULT, 102562306a36Sopenharmony_ci }, { 102662306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB565, 102762306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8, 102862306a36Sopenharmony_ci .bpp = 2, 102962306a36Sopenharmony_ci .swap = ISI_CFG2_YCC_SWAP_MODE_1, 103062306a36Sopenharmony_ci }, { 103162306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_GREY, 103262306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_Y10_1X10, 103362306a36Sopenharmony_ci .bpp = 1, 103462306a36Sopenharmony_ci .swap = ISI_CFG2_GS_MODE_2_PIXEL | ISI_CFG2_GRAYSCALE, 103562306a36Sopenharmony_ci }, { 103662306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_Y16, 103762306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_Y10_1X10, 103862306a36Sopenharmony_ci .bpp = 2, 103962306a36Sopenharmony_ci .swap = ISI_CFG2_GS_MODE_2_PIXEL | ISI_CFG2_GRAYSCALE, 104062306a36Sopenharmony_ci }, 104162306a36Sopenharmony_ci}; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_cistatic int isi_formats_init(struct atmel_isi *isi) 104462306a36Sopenharmony_ci{ 104562306a36Sopenharmony_ci const struct isi_format *isi_fmts[ARRAY_SIZE(isi_formats)]; 104662306a36Sopenharmony_ci unsigned int num_fmts = 0, i, j; 104762306a36Sopenharmony_ci struct v4l2_subdev *subdev = isi->entity.subdev; 104862306a36Sopenharmony_ci struct v4l2_subdev_mbus_code_enum mbus_code = { 104962306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 105062306a36Sopenharmony_ci }; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci while (!v4l2_subdev_call(subdev, pad, enum_mbus_code, 105362306a36Sopenharmony_ci NULL, &mbus_code)) { 105462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(isi_formats); i++) { 105562306a36Sopenharmony_ci if (isi_formats[i].mbus_code != mbus_code.code) 105662306a36Sopenharmony_ci continue; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci /* Code supported, have we got this fourcc yet? */ 105962306a36Sopenharmony_ci for (j = 0; j < num_fmts; j++) 106062306a36Sopenharmony_ci if (isi_fmts[j]->fourcc == isi_formats[i].fourcc) 106162306a36Sopenharmony_ci /* Already available */ 106262306a36Sopenharmony_ci break; 106362306a36Sopenharmony_ci if (j == num_fmts) 106462306a36Sopenharmony_ci /* new */ 106562306a36Sopenharmony_ci isi_fmts[num_fmts++] = isi_formats + i; 106662306a36Sopenharmony_ci } 106762306a36Sopenharmony_ci mbus_code.index++; 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci if (!num_fmts) 107162306a36Sopenharmony_ci return -ENXIO; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci isi->num_user_formats = num_fmts; 107462306a36Sopenharmony_ci isi->user_formats = devm_kcalloc(isi->dev, 107562306a36Sopenharmony_ci num_fmts, sizeof(struct isi_format *), 107662306a36Sopenharmony_ci GFP_KERNEL); 107762306a36Sopenharmony_ci if (!isi->user_formats) 107862306a36Sopenharmony_ci return -ENOMEM; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci memcpy(isi->user_formats, isi_fmts, 108162306a36Sopenharmony_ci num_fmts * sizeof(struct isi_format *)); 108262306a36Sopenharmony_ci isi->current_fmt = isi->user_formats[0]; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci return 0; 108562306a36Sopenharmony_ci} 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_cistatic int isi_graph_notify_complete(struct v4l2_async_notifier *notifier) 108862306a36Sopenharmony_ci{ 108962306a36Sopenharmony_ci struct atmel_isi *isi = notifier_to_isi(notifier); 109062306a36Sopenharmony_ci int ret; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci isi->vdev->ctrl_handler = isi->entity.subdev->ctrl_handler; 109362306a36Sopenharmony_ci ret = isi_formats_init(isi); 109462306a36Sopenharmony_ci if (ret) { 109562306a36Sopenharmony_ci dev_err(isi->dev, "No supported mediabus format found\n"); 109662306a36Sopenharmony_ci return ret; 109762306a36Sopenharmony_ci } 109862306a36Sopenharmony_ci ret = isi_camera_set_bus_param(isi); 109962306a36Sopenharmony_ci if (ret) { 110062306a36Sopenharmony_ci dev_err(isi->dev, "Can't wake up device\n"); 110162306a36Sopenharmony_ci return ret; 110262306a36Sopenharmony_ci } 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci ret = isi_set_default_fmt(isi); 110562306a36Sopenharmony_ci if (ret) { 110662306a36Sopenharmony_ci dev_err(isi->dev, "Could not set default format\n"); 110762306a36Sopenharmony_ci return ret; 110862306a36Sopenharmony_ci } 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci ret = video_register_device(isi->vdev, VFL_TYPE_VIDEO, -1); 111162306a36Sopenharmony_ci if (ret) { 111262306a36Sopenharmony_ci dev_err(isi->dev, "Failed to register video device\n"); 111362306a36Sopenharmony_ci return ret; 111462306a36Sopenharmony_ci } 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci dev_dbg(isi->dev, "Device registered as %s\n", 111762306a36Sopenharmony_ci video_device_node_name(isi->vdev)); 111862306a36Sopenharmony_ci return 0; 111962306a36Sopenharmony_ci} 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_cistatic void isi_graph_notify_unbind(struct v4l2_async_notifier *notifier, 112262306a36Sopenharmony_ci struct v4l2_subdev *sd, 112362306a36Sopenharmony_ci struct v4l2_async_connection *asd) 112462306a36Sopenharmony_ci{ 112562306a36Sopenharmony_ci struct atmel_isi *isi = notifier_to_isi(notifier); 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci dev_dbg(isi->dev, "Removing %s\n", video_device_node_name(isi->vdev)); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci /* Checks internally if vdev have been init or not */ 113062306a36Sopenharmony_ci video_unregister_device(isi->vdev); 113162306a36Sopenharmony_ci} 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_cistatic int isi_graph_notify_bound(struct v4l2_async_notifier *notifier, 113462306a36Sopenharmony_ci struct v4l2_subdev *subdev, 113562306a36Sopenharmony_ci struct v4l2_async_connection *asd) 113662306a36Sopenharmony_ci{ 113762306a36Sopenharmony_ci struct atmel_isi *isi = notifier_to_isi(notifier); 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci dev_dbg(isi->dev, "subdev %s bound\n", subdev->name); 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci isi->entity.subdev = subdev; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci return 0; 114462306a36Sopenharmony_ci} 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_cistatic const struct v4l2_async_notifier_operations isi_graph_notify_ops = { 114762306a36Sopenharmony_ci .bound = isi_graph_notify_bound, 114862306a36Sopenharmony_ci .unbind = isi_graph_notify_unbind, 114962306a36Sopenharmony_ci .complete = isi_graph_notify_complete, 115062306a36Sopenharmony_ci}; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_cistatic int isi_graph_init(struct atmel_isi *isi) 115362306a36Sopenharmony_ci{ 115462306a36Sopenharmony_ci struct v4l2_async_connection *asd; 115562306a36Sopenharmony_ci struct device_node *ep; 115662306a36Sopenharmony_ci int ret; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci ep = of_graph_get_next_endpoint(isi->dev->of_node, NULL); 115962306a36Sopenharmony_ci if (!ep) 116062306a36Sopenharmony_ci return -EINVAL; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci v4l2_async_nf_init(&isi->notifier, &isi->v4l2_dev); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci asd = v4l2_async_nf_add_fwnode_remote(&isi->notifier, 116562306a36Sopenharmony_ci of_fwnode_handle(ep), 116662306a36Sopenharmony_ci struct v4l2_async_connection); 116762306a36Sopenharmony_ci of_node_put(ep); 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci if (IS_ERR(asd)) 117062306a36Sopenharmony_ci return PTR_ERR(asd); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci isi->notifier.ops = &isi_graph_notify_ops; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci ret = v4l2_async_nf_register(&isi->notifier); 117562306a36Sopenharmony_ci if (ret < 0) { 117662306a36Sopenharmony_ci dev_err(isi->dev, "Notifier registration failed\n"); 117762306a36Sopenharmony_ci v4l2_async_nf_cleanup(&isi->notifier); 117862306a36Sopenharmony_ci return ret; 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci return 0; 118262306a36Sopenharmony_ci} 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_cistatic int atmel_isi_probe(struct platform_device *pdev) 118662306a36Sopenharmony_ci{ 118762306a36Sopenharmony_ci int irq; 118862306a36Sopenharmony_ci struct atmel_isi *isi; 118962306a36Sopenharmony_ci struct vb2_queue *q; 119062306a36Sopenharmony_ci int ret, i; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci isi = devm_kzalloc(&pdev->dev, sizeof(struct atmel_isi), GFP_KERNEL); 119362306a36Sopenharmony_ci if (!isi) 119462306a36Sopenharmony_ci return -ENOMEM; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci isi->pclk = devm_clk_get(&pdev->dev, "isi_clk"); 119762306a36Sopenharmony_ci if (IS_ERR(isi->pclk)) 119862306a36Sopenharmony_ci return PTR_ERR(isi->pclk); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci ret = atmel_isi_parse_dt(isi, pdev); 120162306a36Sopenharmony_ci if (ret) 120262306a36Sopenharmony_ci return ret; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci isi->active = NULL; 120562306a36Sopenharmony_ci isi->dev = &pdev->dev; 120662306a36Sopenharmony_ci mutex_init(&isi->lock); 120762306a36Sopenharmony_ci spin_lock_init(&isi->irqlock); 120862306a36Sopenharmony_ci INIT_LIST_HEAD(&isi->video_buffer_list); 120962306a36Sopenharmony_ci INIT_LIST_HEAD(&isi->dma_desc_head); 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci q = &isi->queue; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci /* Initialize the top-level structure */ 121462306a36Sopenharmony_ci ret = v4l2_device_register(&pdev->dev, &isi->v4l2_dev); 121562306a36Sopenharmony_ci if (ret) 121662306a36Sopenharmony_ci return ret; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci isi->vdev = video_device_alloc(); 121962306a36Sopenharmony_ci if (!isi->vdev) { 122062306a36Sopenharmony_ci ret = -ENOMEM; 122162306a36Sopenharmony_ci goto err_vdev_alloc; 122262306a36Sopenharmony_ci } 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci /* video node */ 122562306a36Sopenharmony_ci isi->vdev->fops = &isi_fops; 122662306a36Sopenharmony_ci isi->vdev->v4l2_dev = &isi->v4l2_dev; 122762306a36Sopenharmony_ci isi->vdev->queue = &isi->queue; 122862306a36Sopenharmony_ci strscpy(isi->vdev->name, KBUILD_MODNAME, sizeof(isi->vdev->name)); 122962306a36Sopenharmony_ci isi->vdev->release = video_device_release; 123062306a36Sopenharmony_ci isi->vdev->ioctl_ops = &isi_ioctl_ops; 123162306a36Sopenharmony_ci isi->vdev->lock = &isi->lock; 123262306a36Sopenharmony_ci isi->vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | 123362306a36Sopenharmony_ci V4L2_CAP_READWRITE; 123462306a36Sopenharmony_ci video_set_drvdata(isi->vdev, isi); 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci /* buffer queue */ 123762306a36Sopenharmony_ci q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 123862306a36Sopenharmony_ci q->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; 123962306a36Sopenharmony_ci q->lock = &isi->lock; 124062306a36Sopenharmony_ci q->drv_priv = isi; 124162306a36Sopenharmony_ci q->buf_struct_size = sizeof(struct frame_buffer); 124262306a36Sopenharmony_ci q->ops = &isi_video_qops; 124362306a36Sopenharmony_ci q->mem_ops = &vb2_dma_contig_memops; 124462306a36Sopenharmony_ci q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 124562306a36Sopenharmony_ci q->min_buffers_needed = 2; 124662306a36Sopenharmony_ci q->dev = &pdev->dev; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci ret = vb2_queue_init(q); 124962306a36Sopenharmony_ci if (ret < 0) { 125062306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to initialize VB2 queue\n"); 125162306a36Sopenharmony_ci goto err_vb2_queue; 125262306a36Sopenharmony_ci } 125362306a36Sopenharmony_ci isi->p_fb_descriptors = dma_alloc_coherent(&pdev->dev, 125462306a36Sopenharmony_ci sizeof(struct fbd) * VIDEO_MAX_FRAME, 125562306a36Sopenharmony_ci &isi->fb_descriptors_phys, 125662306a36Sopenharmony_ci GFP_KERNEL); 125762306a36Sopenharmony_ci if (!isi->p_fb_descriptors) { 125862306a36Sopenharmony_ci dev_err(&pdev->dev, "Can't allocate descriptors!\n"); 125962306a36Sopenharmony_ci ret = -ENOMEM; 126062306a36Sopenharmony_ci goto err_dma_alloc; 126162306a36Sopenharmony_ci } 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci for (i = 0; i < VIDEO_MAX_FRAME; i++) { 126462306a36Sopenharmony_ci isi->dma_desc[i].p_fbd = isi->p_fb_descriptors + i; 126562306a36Sopenharmony_ci isi->dma_desc[i].fbd_phys = isi->fb_descriptors_phys + 126662306a36Sopenharmony_ci i * sizeof(struct fbd); 126762306a36Sopenharmony_ci list_add(&isi->dma_desc[i].list, &isi->dma_desc_head); 126862306a36Sopenharmony_ci } 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci isi->regs = devm_platform_ioremap_resource(pdev, 0); 127162306a36Sopenharmony_ci if (IS_ERR(isi->regs)) { 127262306a36Sopenharmony_ci ret = PTR_ERR(isi->regs); 127362306a36Sopenharmony_ci goto err_ioremap; 127462306a36Sopenharmony_ci } 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci if (isi->pdata.data_width_flags & ISI_DATAWIDTH_8) 127762306a36Sopenharmony_ci isi->width_flags = 1 << 7; 127862306a36Sopenharmony_ci if (isi->pdata.data_width_flags & ISI_DATAWIDTH_10) 127962306a36Sopenharmony_ci isi->width_flags |= 1 << 9; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 128262306a36Sopenharmony_ci if (irq < 0) { 128362306a36Sopenharmony_ci ret = irq; 128462306a36Sopenharmony_ci goto err_req_irq; 128562306a36Sopenharmony_ci } 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, isi_interrupt, 0, "isi", isi); 128862306a36Sopenharmony_ci if (ret) { 128962306a36Sopenharmony_ci dev_err(&pdev->dev, "Unable to request irq %d\n", irq); 129062306a36Sopenharmony_ci goto err_req_irq; 129162306a36Sopenharmony_ci } 129262306a36Sopenharmony_ci isi->irq = irq; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci ret = isi_graph_init(isi); 129562306a36Sopenharmony_ci if (ret < 0) 129662306a36Sopenharmony_ci goto err_req_irq; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci pm_suspend_ignore_children(&pdev->dev, true); 129962306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 130062306a36Sopenharmony_ci platform_set_drvdata(pdev, isi); 130162306a36Sopenharmony_ci return 0; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_cierr_req_irq: 130462306a36Sopenharmony_cierr_ioremap: 130562306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, 130662306a36Sopenharmony_ci sizeof(struct fbd) * VIDEO_MAX_FRAME, 130762306a36Sopenharmony_ci isi->p_fb_descriptors, 130862306a36Sopenharmony_ci isi->fb_descriptors_phys); 130962306a36Sopenharmony_cierr_dma_alloc: 131062306a36Sopenharmony_cierr_vb2_queue: 131162306a36Sopenharmony_ci video_device_release(isi->vdev); 131262306a36Sopenharmony_cierr_vdev_alloc: 131362306a36Sopenharmony_ci v4l2_device_unregister(&isi->v4l2_dev); 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci return ret; 131662306a36Sopenharmony_ci} 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_cistatic void atmel_isi_remove(struct platform_device *pdev) 131962306a36Sopenharmony_ci{ 132062306a36Sopenharmony_ci struct atmel_isi *isi = platform_get_drvdata(pdev); 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, 132362306a36Sopenharmony_ci sizeof(struct fbd) * VIDEO_MAX_FRAME, 132462306a36Sopenharmony_ci isi->p_fb_descriptors, 132562306a36Sopenharmony_ci isi->fb_descriptors_phys); 132662306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 132762306a36Sopenharmony_ci v4l2_async_nf_unregister(&isi->notifier); 132862306a36Sopenharmony_ci v4l2_async_nf_cleanup(&isi->notifier); 132962306a36Sopenharmony_ci v4l2_device_unregister(&isi->v4l2_dev); 133062306a36Sopenharmony_ci} 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci#ifdef CONFIG_PM 133362306a36Sopenharmony_cistatic int atmel_isi_runtime_suspend(struct device *dev) 133462306a36Sopenharmony_ci{ 133562306a36Sopenharmony_ci struct atmel_isi *isi = dev_get_drvdata(dev); 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci clk_disable_unprepare(isi->pclk); 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci return 0; 134062306a36Sopenharmony_ci} 134162306a36Sopenharmony_cistatic int atmel_isi_runtime_resume(struct device *dev) 134262306a36Sopenharmony_ci{ 134362306a36Sopenharmony_ci struct atmel_isi *isi = dev_get_drvdata(dev); 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci return clk_prepare_enable(isi->pclk); 134662306a36Sopenharmony_ci} 134762306a36Sopenharmony_ci#endif /* CONFIG_PM */ 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_cistatic const struct dev_pm_ops atmel_isi_dev_pm_ops = { 135062306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(atmel_isi_runtime_suspend, 135162306a36Sopenharmony_ci atmel_isi_runtime_resume, NULL) 135262306a36Sopenharmony_ci}; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_cistatic const struct of_device_id atmel_isi_of_match[] = { 135562306a36Sopenharmony_ci { .compatible = "atmel,at91sam9g45-isi" }, 135662306a36Sopenharmony_ci { } 135762306a36Sopenharmony_ci}; 135862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, atmel_isi_of_match); 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_cistatic struct platform_driver atmel_isi_driver = { 136162306a36Sopenharmony_ci .driver = { 136262306a36Sopenharmony_ci .name = "atmel_isi", 136362306a36Sopenharmony_ci .of_match_table = of_match_ptr(atmel_isi_of_match), 136462306a36Sopenharmony_ci .pm = &atmel_isi_dev_pm_ops, 136562306a36Sopenharmony_ci }, 136662306a36Sopenharmony_ci .probe = atmel_isi_probe, 136762306a36Sopenharmony_ci .remove_new = atmel_isi_remove, 136862306a36Sopenharmony_ci}; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_cimodule_platform_driver(atmel_isi_driver); 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ciMODULE_AUTHOR("Josh Wu <josh.wu@atmel.com>"); 137362306a36Sopenharmony_ciMODULE_DESCRIPTION("The V4L2 driver for Atmel Linux"); 137462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1375