| Thomas Gleixner | d2912cb | 2019-06-04 10:11:33 +0200 | [diff] [blame^] | 1 | // SPDX-License-Identifier: GPL-2.0-only | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 2 | /* | 
 | 3 |  * Copyright (c) 2011 Atmel Corporation | 
 | 4 |  * Josh Wu, <josh.wu@atmel.com> | 
 | 5 |  * | 
 | 6 |  * Based on previous work by Lars Haring, <lars.haring@atmel.com> | 
 | 7 |  * and Sedji Gaouaou | 
 | 8 |  * Based on the bttv driver for Bt848 with respective copyright holders | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 9 |  */ | 
 | 10 |  | 
 | 11 | #include <linux/clk.h> | 
 | 12 | #include <linux/completion.h> | 
 | 13 | #include <linux/delay.h> | 
 | 14 | #include <linux/fs.h> | 
 | 15 | #include <linux/init.h> | 
 | 16 | #include <linux/interrupt.h> | 
 | 17 | #include <linux/kernel.h> | 
 | 18 | #include <linux/module.h> | 
| Sakari Ailus | 859969b | 2016-08-26 20:17:25 -0300 | [diff] [blame] | 19 | #include <linux/of_graph.h> | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 20 | #include <linux/platform_device.h> | 
| Josh Wu | f3745a3a | 2015-05-26 06:54:46 -0300 | [diff] [blame] | 21 | #include <linux/pm_runtime.h> | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 22 | #include <linux/slab.h> | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 23 | #include <linux/of.h> | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 24 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 25 | #include <linux/videodev2.h> | 
 | 26 | #include <media/v4l2-ctrls.h> | 
 | 27 | #include <media/v4l2-device.h> | 
 | 28 | #include <media/v4l2-dev.h> | 
 | 29 | #include <media/v4l2-ioctl.h> | 
 | 30 | #include <media/v4l2-event.h> | 
| Sakari Ailus | 859969b | 2016-08-26 20:17:25 -0300 | [diff] [blame] | 31 | #include <media/v4l2-fwnode.h> | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 32 | #include <media/videobuf2-dma-contig.h> | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 33 | #include <media/v4l2-image-sizes.h> | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 34 |  | 
| Laurent Pinchart | 40a78f3 | 2015-08-01 06:22:54 -0300 | [diff] [blame] | 35 | #include "atmel-isi.h" | 
 | 36 |  | 
| Hugues Fruchet | ec62c9a | 2017-05-19 07:04:52 -0300 | [diff] [blame] | 37 | #define MAX_SUPPORT_WIDTH		2048U | 
 | 38 | #define MAX_SUPPORT_HEIGHT		2048U | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 39 | #define MIN_FRAME_RATE			15 | 
 | 40 | #define FRAME_INTERVAL_MILLI_SEC	(1000 / MIN_FRAME_RATE) | 
 | 41 |  | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 42 | /* Frame buffer descriptor */ | 
 | 43 | struct fbd { | 
 | 44 | 	/* Physical address of the frame buffer */ | 
 | 45 | 	u32 fb_address; | 
 | 46 | 	/* DMA Control Register(only in HISI2) */ | 
 | 47 | 	u32 dma_ctrl; | 
 | 48 | 	/* Physical address of the next fbd */ | 
 | 49 | 	u32 next_fbd_address; | 
 | 50 | }; | 
 | 51 |  | 
 | 52 | static void set_dma_ctrl(struct fbd *fb_desc, u32 ctrl) | 
 | 53 | { | 
 | 54 | 	fb_desc->dma_ctrl = ctrl; | 
 | 55 | } | 
 | 56 |  | 
 | 57 | struct isi_dma_desc { | 
 | 58 | 	struct list_head list; | 
 | 59 | 	struct fbd *p_fbd; | 
| Mauro Carvalho Chehab | 8f05232 | 2014-08-22 05:52:54 -0500 | [diff] [blame] | 60 | 	dma_addr_t fbd_phys; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 61 | }; | 
 | 62 |  | 
 | 63 | /* Frame buffer data */ | 
 | 64 | struct frame_buffer { | 
| Junghak Sung | 2d70071 | 2015-09-22 10:30:30 -0300 | [diff] [blame] | 65 | 	struct vb2_v4l2_buffer vb; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 66 | 	struct isi_dma_desc *p_dma_desc; | 
 | 67 | 	struct list_head list; | 
 | 68 | }; | 
 | 69 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 70 | struct isi_graph_entity { | 
 | 71 | 	struct device_node *node; | 
 | 72 |  | 
 | 73 | 	struct v4l2_async_subdev asd; | 
 | 74 | 	struct v4l2_subdev *subdev; | 
 | 75 | }; | 
 | 76 |  | 
 | 77 | /* | 
 | 78 |  * struct isi_format - ISI media bus format information | 
 | 79 |  * @fourcc:		Fourcc code for this format | 
 | 80 |  * @mbus_code:		V4L2 media bus format code. | 
 | 81 |  * @bpp:		Bytes per pixel (when stored in memory) | 
 | 82 |  * @swap:		Byte swap configuration value | 
 | 83 |  * @support:		Indicates format supported by subdev | 
 | 84 |  * @skip:		Skip duplicate format supported by subdev | 
 | 85 |  */ | 
 | 86 | struct isi_format { | 
 | 87 | 	u32	fourcc; | 
 | 88 | 	u32	mbus_code; | 
 | 89 | 	u8	bpp; | 
 | 90 | 	u32	swap; | 
 | 91 | }; | 
 | 92 |  | 
 | 93 |  | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 94 | struct atmel_isi { | 
 | 95 | 	/* Protects the access of variables shared with the ISR */ | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 96 | 	spinlock_t			irqlock; | 
 | 97 | 	struct device			*dev; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 98 | 	void __iomem			*regs; | 
 | 99 |  | 
 | 100 | 	int				sequence; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 101 |  | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 102 | 	/* Allocate descriptors for dma buffer use */ | 
 | 103 | 	struct fbd			*p_fb_descriptors; | 
| Mauro Carvalho Chehab | 8f05232 | 2014-08-22 05:52:54 -0500 | [diff] [blame] | 104 | 	dma_addr_t			fb_descriptors_phys; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 105 | 	struct				list_head dma_desc_head; | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 106 | 	struct isi_dma_desc		dma_desc[VIDEO_MAX_FRAME]; | 
| Josh Wu | 0fb7257 | 2015-11-03 03:45:09 -0200 | [diff] [blame] | 107 | 	bool				enable_preview_path; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 108 |  | 
 | 109 | 	struct completion		complete; | 
| Mauro Carvalho Chehab | 8b72c18 | 2019-02-18 14:29:00 -0500 | [diff] [blame] | 110 | 	/* ISI peripheral clock */ | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 111 | 	struct clk			*pclk; | 
 | 112 | 	unsigned int			irq; | 
 | 113 |  | 
| Josh Wu | 833e106 | 2014-07-25 07:13:39 -0300 | [diff] [blame] | 114 | 	struct isi_platform_data	pdata; | 
| Guennadi Liakhovetski | a4e9f10 | 2011-07-27 12:18:37 -0300 | [diff] [blame] | 115 | 	u16				width_flags;	/* max 12 bits */ | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 116 |  | 
 | 117 | 	struct list_head		video_buffer_list; | 
 | 118 | 	struct frame_buffer		*active; | 
 | 119 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 120 | 	struct v4l2_device		v4l2_dev; | 
 | 121 | 	struct video_device		*vdev; | 
 | 122 | 	struct v4l2_async_notifier	notifier; | 
 | 123 | 	struct isi_graph_entity		entity; | 
 | 124 | 	struct v4l2_format		fmt; | 
 | 125 |  | 
 | 126 | 	const struct isi_format		**user_formats; | 
 | 127 | 	unsigned int			num_user_formats; | 
 | 128 | 	const struct isi_format		*current_fmt; | 
 | 129 |  | 
 | 130 | 	struct mutex			lock; | 
 | 131 | 	struct vb2_queue		queue; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 132 | }; | 
 | 133 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 134 | #define notifier_to_isi(n) container_of(n, struct atmel_isi, notifier) | 
 | 135 |  | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 136 | static void isi_writel(struct atmel_isi *isi, u32 reg, u32 val) | 
 | 137 | { | 
 | 138 | 	writel(val, isi->regs + reg); | 
 | 139 | } | 
 | 140 | static u32 isi_readl(struct atmel_isi *isi, u32 reg) | 
 | 141 | { | 
 | 142 | 	return readl(isi->regs + reg); | 
 | 143 | } | 
 | 144 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 145 | static void configure_geometry(struct atmel_isi *isi) | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 146 | { | 
| Josh Wu | bd70f26 | 2015-11-03 03:45:10 -0200 | [diff] [blame] | 147 | 	u32 cfg2, psize; | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 148 | 	u32 fourcc = isi->current_fmt->fourcc; | 
| Josh Wu | 05645a4 | 2015-11-03 03:45:12 -0200 | [diff] [blame] | 149 |  | 
 | 150 | 	isi->enable_preview_path = fourcc == V4L2_PIX_FMT_RGB565 || | 
 | 151 | 				   fourcc == V4L2_PIX_FMT_RGB32; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 152 |  | 
| Josh Wu | ad5f9d1 | 2015-09-11 04:00:14 -0300 | [diff] [blame] | 153 | 	/* According to sensor's output format to set cfg2 */ | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 154 | 	cfg2 = isi->current_fmt->swap; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 155 |  | 
 | 156 | 	isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 157 | 	/* Set width */ | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 158 | 	cfg2 |= ((isi->fmt.fmt.pix.width - 1) << ISI_CFG2_IM_HSIZE_OFFSET) & | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 159 | 			ISI_CFG2_IM_HSIZE_MASK; | 
 | 160 | 	/* Set height */ | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 161 | 	cfg2 |= ((isi->fmt.fmt.pix.height - 1) << ISI_CFG2_IM_VSIZE_OFFSET) | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 162 | 			& ISI_CFG2_IM_VSIZE_MASK; | 
 | 163 | 	isi_writel(isi, ISI_CFG2, cfg2); | 
| Josh Wu | bd70f26 | 2015-11-03 03:45:10 -0200 | [diff] [blame] | 164 |  | 
 | 165 | 	/* No down sampling, preview size equal to sensor output size */ | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 166 | 	psize = ((isi->fmt.fmt.pix.width - 1) << ISI_PSIZE_PREV_HSIZE_OFFSET) & | 
| Josh Wu | bd70f26 | 2015-11-03 03:45:10 -0200 | [diff] [blame] | 167 | 		ISI_PSIZE_PREV_HSIZE_MASK; | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 168 | 	psize |= ((isi->fmt.fmt.pix.height - 1) << ISI_PSIZE_PREV_VSIZE_OFFSET) & | 
| Josh Wu | bd70f26 | 2015-11-03 03:45:10 -0200 | [diff] [blame] | 169 | 		ISI_PSIZE_PREV_VSIZE_MASK; | 
 | 170 | 	isi_writel(isi, ISI_PSIZE, psize); | 
 | 171 | 	isi_writel(isi, ISI_PDECF, ISI_PDECF_NO_SAMPLING); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 172 | } | 
 | 173 |  | 
 | 174 | static irqreturn_t atmel_isi_handle_streaming(struct atmel_isi *isi) | 
 | 175 | { | 
 | 176 | 	if (isi->active) { | 
| Junghak Sung | 2d70071 | 2015-09-22 10:30:30 -0300 | [diff] [blame] | 177 | 		struct vb2_v4l2_buffer *vbuf = &isi->active->vb; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 178 | 		struct frame_buffer *buf = isi->active; | 
 | 179 |  | 
 | 180 | 		list_del_init(&buf->list); | 
| Junghak Sung | d6dd645 | 2015-11-03 08:16:37 -0200 | [diff] [blame] | 181 | 		vbuf->vb2_buf.timestamp = ktime_get_ns(); | 
| Junghak Sung | 2d70071 | 2015-09-22 10:30:30 -0300 | [diff] [blame] | 182 | 		vbuf->sequence = isi->sequence++; | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 183 | 		vbuf->field = V4L2_FIELD_NONE; | 
| Junghak Sung | 2d70071 | 2015-09-22 10:30:30 -0300 | [diff] [blame] | 184 | 		vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 185 | 	} | 
 | 186 |  | 
 | 187 | 	if (list_empty(&isi->video_buffer_list)) { | 
 | 188 | 		isi->active = NULL; | 
 | 189 | 	} else { | 
 | 190 | 		/* start next dma frame. */ | 
 | 191 | 		isi->active = list_entry(isi->video_buffer_list.next, | 
 | 192 | 					struct frame_buffer, list); | 
| Josh Wu | 0fb7257 | 2015-11-03 03:45:09 -0200 | [diff] [blame] | 193 | 		if (!isi->enable_preview_path) { | 
 | 194 | 			isi_writel(isi, ISI_DMA_C_DSCR, | 
 | 195 | 				(u32)isi->active->p_dma_desc->fbd_phys); | 
 | 196 | 			isi_writel(isi, ISI_DMA_C_CTRL, | 
 | 197 | 				ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE); | 
 | 198 | 			isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH); | 
 | 199 | 		} else { | 
 | 200 | 			isi_writel(isi, ISI_DMA_P_DSCR, | 
 | 201 | 				(u32)isi->active->p_dma_desc->fbd_phys); | 
 | 202 | 			isi_writel(isi, ISI_DMA_P_CTRL, | 
 | 203 | 				ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE); | 
 | 204 | 			isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_P_CH); | 
 | 205 | 		} | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 206 | 	} | 
 | 207 | 	return IRQ_HANDLED; | 
 | 208 | } | 
 | 209 |  | 
 | 210 | /* ISI interrupt service routine */ | 
 | 211 | static irqreturn_t isi_interrupt(int irq, void *dev_id) | 
 | 212 | { | 
 | 213 | 	struct atmel_isi *isi = dev_id; | 
 | 214 | 	u32 status, mask, pending; | 
 | 215 | 	irqreturn_t ret = IRQ_NONE; | 
 | 216 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 217 | 	spin_lock(&isi->irqlock); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 218 |  | 
 | 219 | 	status = isi_readl(isi, ISI_STATUS); | 
 | 220 | 	mask = isi_readl(isi, ISI_INTMASK); | 
 | 221 | 	pending = status & mask; | 
 | 222 |  | 
 | 223 | 	if (pending & ISI_CTRL_SRST) { | 
 | 224 | 		complete(&isi->complete); | 
 | 225 | 		isi_writel(isi, ISI_INTDIS, ISI_CTRL_SRST); | 
 | 226 | 		ret = IRQ_HANDLED; | 
 | 227 | 	} else if (pending & ISI_CTRL_DIS) { | 
 | 228 | 		complete(&isi->complete); | 
 | 229 | 		isi_writel(isi, ISI_INTDIS, ISI_CTRL_DIS); | 
 | 230 | 		ret = IRQ_HANDLED; | 
 | 231 | 	} else { | 
| Josh Wu | 0fb7257 | 2015-11-03 03:45:09 -0200 | [diff] [blame] | 232 | 		if (likely(pending & ISI_SR_CXFR_DONE) || | 
 | 233 | 				likely(pending & ISI_SR_PXFR_DONE)) | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 234 | 			ret = atmel_isi_handle_streaming(isi); | 
 | 235 | 	} | 
 | 236 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 237 | 	spin_unlock(&isi->irqlock); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 238 | 	return ret; | 
 | 239 | } | 
 | 240 |  | 
 | 241 | #define	WAIT_ISI_RESET		1 | 
 | 242 | #define	WAIT_ISI_DISABLE	0 | 
 | 243 | static int atmel_isi_wait_status(struct atmel_isi *isi, int wait_reset) | 
 | 244 | { | 
 | 245 | 	unsigned long timeout; | 
 | 246 | 	/* | 
 | 247 | 	 * The reset or disable will only succeed if we have a | 
 | 248 | 	 * pixel clock from the camera. | 
 | 249 | 	 */ | 
 | 250 | 	init_completion(&isi->complete); | 
 | 251 |  | 
 | 252 | 	if (wait_reset) { | 
 | 253 | 		isi_writel(isi, ISI_INTEN, ISI_CTRL_SRST); | 
 | 254 | 		isi_writel(isi, ISI_CTRL, ISI_CTRL_SRST); | 
 | 255 | 	} else { | 
 | 256 | 		isi_writel(isi, ISI_INTEN, ISI_CTRL_DIS); | 
 | 257 | 		isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); | 
 | 258 | 	} | 
 | 259 |  | 
 | 260 | 	timeout = wait_for_completion_timeout(&isi->complete, | 
| Josh Wu | d67b488 | 2015-06-17 07:27:08 -0300 | [diff] [blame] | 261 | 			msecs_to_jiffies(500)); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 262 | 	if (timeout == 0) | 
 | 263 | 		return -ETIMEDOUT; | 
 | 264 |  | 
 | 265 | 	return 0; | 
 | 266 | } | 
 | 267 |  | 
 | 268 | /* ------------------------------------------------------------------ | 
 | 269 | 	Videobuf operations | 
 | 270 |    ------------------------------------------------------------------*/ | 
| Hans Verkuil | df9ecb0 | 2015-10-28 00:50:37 -0200 | [diff] [blame] | 271 | static int queue_setup(struct vb2_queue *vq, | 
| Guennadi Liakhovetski | fc714e70 | 2011-08-24 10:30:21 -0300 | [diff] [blame] | 272 | 				unsigned int *nbuffers, unsigned int *nplanes, | 
| Hans Verkuil | 36c0f8b | 2016-04-15 09:15:05 -0300 | [diff] [blame] | 273 | 				unsigned int sizes[], struct device *alloc_devs[]) | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 274 | { | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 275 | 	struct atmel_isi *isi = vb2_get_drv_priv(vq); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 276 | 	unsigned long size; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 277 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 278 | 	size = isi->fmt.fmt.pix.sizeimage; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 279 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 280 | 	/* Make sure the image size is large enough. */ | 
 | 281 | 	if (*nplanes) | 
 | 282 | 		return sizes[0] < size ? -EINVAL : 0; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 283 |  | 
 | 284 | 	*nplanes = 1; | 
 | 285 | 	sizes[0] = size; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 286 |  | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 287 | 	isi->active = NULL; | 
 | 288 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 289 | 	dev_dbg(isi->dev, "%s, count=%d, size=%ld\n", __func__, | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 290 | 		*nbuffers, size); | 
 | 291 |  | 
 | 292 | 	return 0; | 
 | 293 | } | 
 | 294 |  | 
 | 295 | static int buffer_init(struct vb2_buffer *vb) | 
 | 296 | { | 
| Junghak Sung | 2d70071 | 2015-09-22 10:30:30 -0300 | [diff] [blame] | 297 | 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); | 
 | 298 | 	struct frame_buffer *buf = container_of(vbuf, struct frame_buffer, vb); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 299 |  | 
 | 300 | 	buf->p_dma_desc = NULL; | 
 | 301 | 	INIT_LIST_HEAD(&buf->list); | 
 | 302 |  | 
 | 303 | 	return 0; | 
 | 304 | } | 
 | 305 |  | 
 | 306 | static int buffer_prepare(struct vb2_buffer *vb) | 
 | 307 | { | 
| Junghak Sung | 2d70071 | 2015-09-22 10:30:30 -0300 | [diff] [blame] | 308 | 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); | 
| Junghak Sung | 2d70071 | 2015-09-22 10:30:30 -0300 | [diff] [blame] | 309 | 	struct frame_buffer *buf = container_of(vbuf, struct frame_buffer, vb); | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 310 | 	struct atmel_isi *isi = vb2_get_drv_priv(vb->vb2_queue); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 311 | 	unsigned long size; | 
 | 312 | 	struct isi_dma_desc *desc; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 313 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 314 | 	size = isi->fmt.fmt.pix.sizeimage; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 315 |  | 
 | 316 | 	if (vb2_plane_size(vb, 0) < size) { | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 317 | 		dev_err(isi->dev, "%s data will not fit into plane (%lu < %lu)\n", | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 318 | 				__func__, vb2_plane_size(vb, 0), size); | 
 | 319 | 		return -EINVAL; | 
 | 320 | 	} | 
 | 321 |  | 
| Junghak Sung | 2d70071 | 2015-09-22 10:30:30 -0300 | [diff] [blame] | 322 | 	vb2_set_plane_payload(vb, 0, size); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 323 |  | 
 | 324 | 	if (!buf->p_dma_desc) { | 
 | 325 | 		if (list_empty(&isi->dma_desc_head)) { | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 326 | 			dev_err(isi->dev, "Not enough dma descriptors.\n"); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 327 | 			return -EINVAL; | 
 | 328 | 		} else { | 
 | 329 | 			/* Get an available descriptor */ | 
 | 330 | 			desc = list_entry(isi->dma_desc_head.next, | 
 | 331 | 						struct isi_dma_desc, list); | 
 | 332 | 			/* Delete the descriptor since now it is used */ | 
 | 333 | 			list_del_init(&desc->list); | 
 | 334 |  | 
 | 335 | 			/* Initialize the dma descriptor */ | 
 | 336 | 			desc->p_fbd->fb_address = | 
| Marek Szyprowski | ba7fcb0 | 2011-08-29 03:20:56 -0300 | [diff] [blame] | 337 | 					vb2_dma_contig_plane_dma_addr(vb, 0); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 338 | 			desc->p_fbd->next_fbd_address = 0; | 
 | 339 | 			set_dma_ctrl(desc->p_fbd, ISI_DMA_CTRL_WB); | 
 | 340 |  | 
 | 341 | 			buf->p_dma_desc = desc; | 
 | 342 | 		} | 
 | 343 | 	} | 
 | 344 | 	return 0; | 
 | 345 | } | 
 | 346 |  | 
 | 347 | static void buffer_cleanup(struct vb2_buffer *vb) | 
 | 348 | { | 
| Junghak Sung | 2d70071 | 2015-09-22 10:30:30 -0300 | [diff] [blame] | 349 | 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 350 | 	struct atmel_isi *isi = vb2_get_drv_priv(vb->vb2_queue); | 
| Junghak Sung | 2d70071 | 2015-09-22 10:30:30 -0300 | [diff] [blame] | 351 | 	struct frame_buffer *buf = container_of(vbuf, struct frame_buffer, vb); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 352 |  | 
 | 353 | 	/* This descriptor is available now and we add to head list */ | 
 | 354 | 	if (buf->p_dma_desc) | 
 | 355 | 		list_add(&buf->p_dma_desc->list, &isi->dma_desc_head); | 
 | 356 | } | 
 | 357 |  | 
 | 358 | static void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer) | 
 | 359 | { | 
 | 360 | 	u32 ctrl, cfg1; | 
 | 361 |  | 
 | 362 | 	cfg1 = isi_readl(isi, ISI_CFG1); | 
 | 363 | 	/* Enable irq: cxfr for the codec path, pxfr for the preview path */ | 
 | 364 | 	isi_writel(isi, ISI_INTEN, | 
 | 365 | 			ISI_SR_CXFR_DONE | ISI_SR_PXFR_DONE); | 
 | 366 |  | 
 | 367 | 	/* Check if already in a frame */ | 
| Josh Wu | 0fb7257 | 2015-11-03 03:45:09 -0200 | [diff] [blame] | 368 | 	if (!isi->enable_preview_path) { | 
 | 369 | 		if (isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) { | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 370 | 			dev_err(isi->dev, "Already in frame handling.\n"); | 
| Josh Wu | 0fb7257 | 2015-11-03 03:45:09 -0200 | [diff] [blame] | 371 | 			return; | 
 | 372 | 		} | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 373 |  | 
| Josh Wu | 0fb7257 | 2015-11-03 03:45:09 -0200 | [diff] [blame] | 374 | 		isi_writel(isi, ISI_DMA_C_DSCR, | 
 | 375 | 				(u32)buffer->p_dma_desc->fbd_phys); | 
 | 376 | 		isi_writel(isi, ISI_DMA_C_CTRL, | 
 | 377 | 				ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE); | 
 | 378 | 		isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH); | 
 | 379 | 	} else { | 
 | 380 | 		isi_writel(isi, ISI_DMA_P_DSCR, | 
 | 381 | 				(u32)buffer->p_dma_desc->fbd_phys); | 
 | 382 | 		isi_writel(isi, ISI_DMA_P_CTRL, | 
 | 383 | 				ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE); | 
 | 384 | 		isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_P_CH); | 
 | 385 | 	} | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 386 |  | 
| Josh Wu | bd6f274 | 2013-12-10 09:25:47 -0300 | [diff] [blame] | 387 | 	cfg1 &= ~ISI_CFG1_FRATE_DIV_MASK; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 388 | 	/* Enable linked list */ | 
| Josh Wu | 833e106 | 2014-07-25 07:13:39 -0300 | [diff] [blame] | 389 | 	cfg1 |= isi->pdata.frate | ISI_CFG1_DISCR; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 390 |  | 
| Josh Wu | 0fb7257 | 2015-11-03 03:45:09 -0200 | [diff] [blame] | 391 | 	/* Enable ISI */ | 
 | 392 | 	ctrl = ISI_CTRL_EN; | 
 | 393 |  | 
 | 394 | 	if (!isi->enable_preview_path) | 
 | 395 | 		ctrl |= ISI_CTRL_CDC; | 
 | 396 |  | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 397 | 	isi_writel(isi, ISI_CTRL, ctrl); | 
 | 398 | 	isi_writel(isi, ISI_CFG1, cfg1); | 
 | 399 | } | 
 | 400 |  | 
 | 401 | static void buffer_queue(struct vb2_buffer *vb) | 
 | 402 | { | 
| Junghak Sung | 2d70071 | 2015-09-22 10:30:30 -0300 | [diff] [blame] | 403 | 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 404 | 	struct atmel_isi *isi = vb2_get_drv_priv(vb->vb2_queue); | 
| Junghak Sung | 2d70071 | 2015-09-22 10:30:30 -0300 | [diff] [blame] | 405 | 	struct frame_buffer *buf = container_of(vbuf, struct frame_buffer, vb); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 406 | 	unsigned long flags = 0; | 
 | 407 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 408 | 	spin_lock_irqsave(&isi->irqlock, flags); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 409 | 	list_add_tail(&buf->list, &isi->video_buffer_list); | 
 | 410 |  | 
| Markus Elfring | af28c99 | 2017-08-28 06:50:28 -0400 | [diff] [blame] | 411 | 	if (!isi->active) { | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 412 | 		isi->active = buf; | 
| Marek Szyprowski | bd323e2 | 2011-08-29 08:51:49 -0300 | [diff] [blame] | 413 | 		if (vb2_is_streaming(vb->vb2_queue)) | 
 | 414 | 			start_dma(isi, buf); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 415 | 	} | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 416 | 	spin_unlock_irqrestore(&isi->irqlock, flags); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 417 | } | 
 | 418 |  | 
| Marek Szyprowski | bd323e2 | 2011-08-29 08:51:49 -0300 | [diff] [blame] | 419 | static int start_streaming(struct vb2_queue *vq, unsigned int count) | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 420 | { | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 421 | 	struct atmel_isi *isi = vb2_get_drv_priv(vq); | 
 | 422 | 	struct frame_buffer *buf, *node; | 
| Laurent Pinchart | c768626 | 2013-11-02 21:25:06 -0300 | [diff] [blame] | 423 | 	int ret; | 
 | 424 |  | 
| Hugues Fruchet | ec62c9a | 2017-05-19 07:04:52 -0300 | [diff] [blame] | 425 | 	pm_runtime_get_sync(isi->dev); | 
 | 426 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 427 | 	/* Enable stream on the sub device */ | 
 | 428 | 	ret = v4l2_subdev_call(isi->entity.subdev, video, s_stream, 1); | 
 | 429 | 	if (ret && ret != -ENOIOCTLCMD) { | 
 | 430 | 		dev_err(isi->dev, "stream on failed in subdev\n"); | 
 | 431 | 		goto err_start_stream; | 
 | 432 | 	} | 
 | 433 |  | 
| Laurent Pinchart | c768626 | 2013-11-02 21:25:06 -0300 | [diff] [blame] | 434 | 	/* Reset ISI */ | 
 | 435 | 	ret = atmel_isi_wait_status(isi, WAIT_ISI_RESET); | 
 | 436 | 	if (ret < 0) { | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 437 | 		dev_err(isi->dev, "Reset ISI timed out\n"); | 
 | 438 | 		goto err_reset; | 
| Laurent Pinchart | c768626 | 2013-11-02 21:25:06 -0300 | [diff] [blame] | 439 | 	} | 
 | 440 | 	/* Disable all interrupts */ | 
| Mauro Carvalho Chehab | 9842a41 | 2014-08-22 05:53:27 -0500 | [diff] [blame] | 441 | 	isi_writel(isi, ISI_INTDIS, (u32)~0UL); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 442 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 443 | 	isi->sequence = 0; | 
 | 444 | 	configure_geometry(isi); | 
| Josh Wu | f653da3 | 2015-09-11 04:00:15 -0300 | [diff] [blame] | 445 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 446 | 	spin_lock_irq(&isi->irqlock); | 
| Josh Wu | 1426f61b | 2013-10-24 04:27:11 -0300 | [diff] [blame] | 447 | 	/* Clear any pending interrupt */ | 
| Mauro Carvalho Chehab | b91677a | 2014-08-26 11:21:43 -0300 | [diff] [blame] | 448 | 	isi_readl(isi, ISI_STATUS); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 449 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 450 | 	start_dma(isi, isi->active); | 
 | 451 | 	spin_unlock_irq(&isi->irqlock); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 452 |  | 
 | 453 | 	return 0; | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 454 |  | 
 | 455 | err_reset: | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 456 | 	v4l2_subdev_call(isi->entity.subdev, video, s_stream, 0); | 
 | 457 |  | 
 | 458 | err_start_stream: | 
| Hugues Fruchet | ec62c9a | 2017-05-19 07:04:52 -0300 | [diff] [blame] | 459 | 	pm_runtime_put(isi->dev); | 
 | 460 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 461 | 	spin_lock_irq(&isi->irqlock); | 
 | 462 | 	isi->active = NULL; | 
 | 463 | 	/* Release all active buffers */ | 
 | 464 | 	list_for_each_entry_safe(buf, node, &isi->video_buffer_list, list) { | 
 | 465 | 		list_del_init(&buf->list); | 
 | 466 | 		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); | 
 | 467 | 	} | 
 | 468 | 	spin_unlock_irq(&isi->irqlock); | 
 | 469 |  | 
 | 470 | 	return ret; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 471 | } | 
 | 472 |  | 
 | 473 | /* abort streaming and wait for last buffer */ | 
| Hans Verkuil | e37559b | 2014-04-17 02:47:21 -0300 | [diff] [blame] | 474 | static void stop_streaming(struct vb2_queue *vq) | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 475 | { | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 476 | 	struct atmel_isi *isi = vb2_get_drv_priv(vq); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 477 | 	struct frame_buffer *buf, *node; | 
 | 478 | 	int ret = 0; | 
 | 479 | 	unsigned long timeout; | 
 | 480 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 481 | 	/* Disable stream on the sub device */ | 
 | 482 | 	ret = v4l2_subdev_call(isi->entity.subdev, video, s_stream, 0); | 
 | 483 | 	if (ret && ret != -ENOIOCTLCMD) | 
 | 484 | 		dev_err(isi->dev, "stream off failed in subdev\n"); | 
 | 485 |  | 
 | 486 | 	spin_lock_irq(&isi->irqlock); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 487 | 	isi->active = NULL; | 
 | 488 | 	/* Release all active buffers */ | 
 | 489 | 	list_for_each_entry_safe(buf, node, &isi->video_buffer_list, list) { | 
 | 490 | 		list_del_init(&buf->list); | 
| Junghak Sung | 2d70071 | 2015-09-22 10:30:30 -0300 | [diff] [blame] | 491 | 		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 492 | 	} | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 493 | 	spin_unlock_irq(&isi->irqlock); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 494 |  | 
| Josh Wu | 0fb7257 | 2015-11-03 03:45:09 -0200 | [diff] [blame] | 495 | 	if (!isi->enable_preview_path) { | 
 | 496 | 		timeout = jiffies + FRAME_INTERVAL_MILLI_SEC * HZ; | 
 | 497 | 		/* Wait until the end of the current frame. */ | 
 | 498 | 		while ((isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) && | 
 | 499 | 				time_before(jiffies, timeout)) | 
 | 500 | 			msleep(1); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 501 |  | 
| Josh Wu | 0fb7257 | 2015-11-03 03:45:09 -0200 | [diff] [blame] | 502 | 		if (time_after(jiffies, timeout)) | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 503 | 			dev_err(isi->dev, | 
| Josh Wu | 0fb7257 | 2015-11-03 03:45:09 -0200 | [diff] [blame] | 504 | 				"Timeout waiting for finishing codec request\n"); | 
 | 505 | 	} | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 506 |  | 
 | 507 | 	/* Disable interrupts */ | 
 | 508 | 	isi_writel(isi, ISI_INTDIS, | 
 | 509 | 			ISI_SR_CXFR_DONE | ISI_SR_PXFR_DONE); | 
 | 510 |  | 
 | 511 | 	/* Disable ISI and wait for it is done */ | 
 | 512 | 	ret = atmel_isi_wait_status(isi, WAIT_ISI_DISABLE); | 
 | 513 | 	if (ret < 0) | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 514 | 		dev_err(isi->dev, "Disable ISI timed out\n"); | 
| Josh Wu | f3745a3a | 2015-05-26 06:54:46 -0300 | [diff] [blame] | 515 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 516 | 	pm_runtime_put(isi->dev); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 517 | } | 
 | 518 |  | 
| Julia Lawall | b7b361f | 2016-09-08 20:59:10 -0300 | [diff] [blame] | 519 | static const struct vb2_ops isi_video_qops = { | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 520 | 	.queue_setup		= queue_setup, | 
 | 521 | 	.buf_init		= buffer_init, | 
 | 522 | 	.buf_prepare		= buffer_prepare, | 
 | 523 | 	.buf_cleanup		= buffer_cleanup, | 
 | 524 | 	.buf_queue		= buffer_queue, | 
 | 525 | 	.start_streaming	= start_streaming, | 
 | 526 | 	.stop_streaming		= stop_streaming, | 
| Lad, Prabhakar | 976036d | 2014-11-26 19:42:27 -0300 | [diff] [blame] | 527 | 	.wait_prepare		= vb2_ops_wait_prepare, | 
 | 528 | 	.wait_finish		= vb2_ops_wait_finish, | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 529 | }; | 
 | 530 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 531 | static int isi_g_fmt_vid_cap(struct file *file, void *priv, | 
 | 532 | 			      struct v4l2_format *fmt) | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 533 | { | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 534 | 	struct atmel_isi *isi = video_drvdata(file); | 
| Lad, Prabhakar | 976036d | 2014-11-26 19:42:27 -0300 | [diff] [blame] | 535 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 536 | 	*fmt = isi->fmt; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 537 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 538 | 	return 0; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 539 | } | 
 | 540 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 541 | static const struct isi_format *find_format_by_fourcc(struct atmel_isi *isi, | 
 | 542 | 						      unsigned int fourcc) | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 543 | { | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 544 | 	unsigned int num_formats = isi->num_user_formats; | 
 | 545 | 	const struct isi_format *fmt; | 
 | 546 | 	unsigned int i; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 547 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 548 | 	for (i = 0; i < num_formats; i++) { | 
 | 549 | 		fmt = isi->user_formats[i]; | 
 | 550 | 		if (fmt->fourcc == fourcc) | 
 | 551 | 			return fmt; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 552 | 	} | 
 | 553 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 554 | 	return NULL; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 555 | } | 
 | 556 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 557 | static int isi_try_fmt(struct atmel_isi *isi, struct v4l2_format *f, | 
 | 558 | 		       const struct isi_format **current_fmt) | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 559 | { | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 560 | 	const struct isi_format *isi_fmt; | 
 | 561 | 	struct v4l2_pix_format *pixfmt = &f->fmt.pix; | 
| Hans Verkuil | 5eab498 | 2015-04-09 04:05:35 -0300 | [diff] [blame] | 562 | 	struct v4l2_subdev_pad_config pad_cfg; | 
 | 563 | 	struct v4l2_subdev_format format = { | 
 | 564 | 		.which = V4L2_SUBDEV_FORMAT_TRY, | 
 | 565 | 	}; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 566 | 	int ret; | 
 | 567 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 568 | 	isi_fmt = find_format_by_fourcc(isi, pixfmt->pixelformat); | 
 | 569 | 	if (!isi_fmt) { | 
 | 570 | 		isi_fmt = isi->user_formats[isi->num_user_formats - 1]; | 
 | 571 | 		pixfmt->pixelformat = isi_fmt->fourcc; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 572 | 	} | 
 | 573 |  | 
| Hugues Fruchet | ec62c9a | 2017-05-19 07:04:52 -0300 | [diff] [blame] | 574 | 	/* Limit to Atmel ISI hardware capabilities */ | 
 | 575 | 	pixfmt->width = clamp(pixfmt->width, 0U, MAX_SUPPORT_WIDTH); | 
 | 576 | 	pixfmt->height = clamp(pixfmt->height, 0U, MAX_SUPPORT_HEIGHT); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 577 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 578 | 	v4l2_fill_mbus_format(&format.format, pixfmt, isi_fmt->mbus_code); | 
 | 579 | 	ret = v4l2_subdev_call(isi->entity.subdev, pad, set_fmt, | 
 | 580 | 			       &pad_cfg, &format); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 581 | 	if (ret < 0) | 
 | 582 | 		return ret; | 
 | 583 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 584 | 	v4l2_fill_pix_format(pixfmt, &format.format); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 585 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 586 | 	pixfmt->field = V4L2_FIELD_NONE; | 
 | 587 | 	pixfmt->bytesperline = pixfmt->width * isi_fmt->bpp; | 
 | 588 | 	pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 589 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 590 | 	if (current_fmt) | 
 | 591 | 		*current_fmt = isi_fmt; | 
 | 592 |  | 
 | 593 | 	return 0; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 594 | } | 
 | 595 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 596 | static int isi_set_fmt(struct atmel_isi *isi, struct v4l2_format *f) | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 597 | { | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 598 | 	struct v4l2_subdev_format format = { | 
| Hans Verkuil | ebcff5f | 2015-04-09 04:01:33 -0300 | [diff] [blame] | 599 | 		.which = V4L2_SUBDEV_FORMAT_ACTIVE, | 
| Hans Verkuil | ebcff5f | 2015-04-09 04:01:33 -0300 | [diff] [blame] | 600 | 	}; | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 601 | 	const struct isi_format *current_fmt; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 602 | 	int ret; | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 603 |  | 
 | 604 | 	ret = isi_try_fmt(isi, f, ¤t_fmt); | 
 | 605 | 	if (ret) | 
 | 606 | 		return ret; | 
 | 607 |  | 
 | 608 | 	v4l2_fill_mbus_format(&format.format, &f->fmt.pix, | 
 | 609 | 			      current_fmt->mbus_code); | 
 | 610 | 	ret = v4l2_subdev_call(isi->entity.subdev, pad, | 
 | 611 | 			       set_fmt, NULL, &format); | 
 | 612 | 	if (ret < 0) | 
 | 613 | 		return ret; | 
 | 614 |  | 
 | 615 | 	isi->fmt = *f; | 
 | 616 | 	isi->current_fmt = current_fmt; | 
 | 617 |  | 
 | 618 | 	return 0; | 
 | 619 | } | 
 | 620 |  | 
 | 621 | static int isi_s_fmt_vid_cap(struct file *file, void *priv, | 
 | 622 | 			      struct v4l2_format *f) | 
 | 623 | { | 
 | 624 | 	struct atmel_isi *isi = video_drvdata(file); | 
 | 625 |  | 
 | 626 | 	if (vb2_is_streaming(&isi->queue)) | 
 | 627 | 		return -EBUSY; | 
 | 628 |  | 
 | 629 | 	return isi_set_fmt(isi, f); | 
 | 630 | } | 
 | 631 |  | 
 | 632 | static int isi_try_fmt_vid_cap(struct file *file, void *priv, | 
 | 633 | 				struct v4l2_format *f) | 
 | 634 | { | 
 | 635 | 	struct atmel_isi *isi = video_drvdata(file); | 
 | 636 |  | 
 | 637 | 	return isi_try_fmt(isi, f, NULL); | 
 | 638 | } | 
 | 639 |  | 
 | 640 | static int isi_enum_fmt_vid_cap(struct file *file, void  *priv, | 
 | 641 | 				struct v4l2_fmtdesc *f) | 
 | 642 | { | 
 | 643 | 	struct atmel_isi *isi = video_drvdata(file); | 
 | 644 |  | 
 | 645 | 	if (f->index >= isi->num_user_formats) | 
 | 646 | 		return -EINVAL; | 
 | 647 |  | 
 | 648 | 	f->pixelformat = isi->user_formats[f->index]->fourcc; | 
 | 649 | 	return 0; | 
 | 650 | } | 
 | 651 |  | 
 | 652 | static int isi_querycap(struct file *file, void *priv, | 
 | 653 | 			struct v4l2_capability *cap) | 
 | 654 | { | 
| Mauro Carvalho Chehab | c0decac | 2018-09-10 08:19:14 -0400 | [diff] [blame] | 655 | 	strscpy(cap->driver, "atmel-isi", sizeof(cap->driver)); | 
 | 656 | 	strscpy(cap->card, "Atmel Image Sensor Interface", sizeof(cap->card)); | 
 | 657 | 	strscpy(cap->bus_info, "platform:isi", sizeof(cap->bus_info)); | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 658 | 	return 0; | 
 | 659 | } | 
 | 660 |  | 
 | 661 | static int isi_enum_input(struct file *file, void *priv, | 
 | 662 | 			   struct v4l2_input *i) | 
 | 663 | { | 
 | 664 | 	if (i->index != 0) | 
 | 665 | 		return -EINVAL; | 
 | 666 |  | 
 | 667 | 	i->type = V4L2_INPUT_TYPE_CAMERA; | 
| Mauro Carvalho Chehab | c0decac | 2018-09-10 08:19:14 -0400 | [diff] [blame] | 668 | 	strscpy(i->name, "Camera", sizeof(i->name)); | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 669 | 	return 0; | 
 | 670 | } | 
 | 671 |  | 
 | 672 | static int isi_g_input(struct file *file, void *priv, unsigned int *i) | 
 | 673 | { | 
 | 674 | 	*i = 0; | 
 | 675 | 	return 0; | 
 | 676 | } | 
 | 677 |  | 
 | 678 | static int isi_s_input(struct file *file, void *priv, unsigned int i) | 
 | 679 | { | 
 | 680 | 	if (i > 0) | 
 | 681 | 		return -EINVAL; | 
 | 682 | 	return 0; | 
 | 683 | } | 
 | 684 |  | 
 | 685 | static int isi_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) | 
 | 686 | { | 
 | 687 | 	struct atmel_isi *isi = video_drvdata(file); | 
 | 688 |  | 
| Hans Verkuil | 4471109 | 2018-01-22 04:00:45 -0500 | [diff] [blame] | 689 | 	return v4l2_g_parm_cap(video_devdata(file), isi->entity.subdev, a); | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 690 | } | 
 | 691 |  | 
 | 692 | static int isi_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) | 
 | 693 | { | 
 | 694 | 	struct atmel_isi *isi = video_drvdata(file); | 
 | 695 |  | 
| Hans Verkuil | 4471109 | 2018-01-22 04:00:45 -0500 | [diff] [blame] | 696 | 	return v4l2_s_parm_cap(video_devdata(file), isi->entity.subdev, a); | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 697 | } | 
 | 698 |  | 
 | 699 | static int isi_enum_framesizes(struct file *file, void *fh, | 
 | 700 | 			       struct v4l2_frmsizeenum *fsize) | 
 | 701 | { | 
 | 702 | 	struct atmel_isi *isi = video_drvdata(file); | 
 | 703 | 	const struct isi_format *isi_fmt; | 
 | 704 | 	struct v4l2_subdev_frame_size_enum fse = { | 
 | 705 | 		.index = fsize->index, | 
 | 706 | 		.which = V4L2_SUBDEV_FORMAT_ACTIVE, | 
 | 707 | 	}; | 
 | 708 | 	int ret; | 
 | 709 |  | 
 | 710 | 	isi_fmt = find_format_by_fourcc(isi, fsize->pixel_format); | 
 | 711 | 	if (!isi_fmt) | 
 | 712 | 		return -EINVAL; | 
 | 713 |  | 
 | 714 | 	fse.code = isi_fmt->mbus_code; | 
 | 715 |  | 
 | 716 | 	ret = v4l2_subdev_call(isi->entity.subdev, pad, enum_frame_size, | 
 | 717 | 			       NULL, &fse); | 
 | 718 | 	if (ret) | 
 | 719 | 		return ret; | 
 | 720 |  | 
 | 721 | 	fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; | 
 | 722 | 	fsize->discrete.width = fse.max_width; | 
 | 723 | 	fsize->discrete.height = fse.max_height; | 
 | 724 |  | 
 | 725 | 	return 0; | 
 | 726 | } | 
 | 727 |  | 
 | 728 | static int isi_enum_frameintervals(struct file *file, void *fh, | 
 | 729 | 				    struct v4l2_frmivalenum *fival) | 
 | 730 | { | 
 | 731 | 	struct atmel_isi *isi = video_drvdata(file); | 
 | 732 | 	const struct isi_format *isi_fmt; | 
 | 733 | 	struct v4l2_subdev_frame_interval_enum fie = { | 
 | 734 | 		.index = fival->index, | 
 | 735 | 		.width = fival->width, | 
 | 736 | 		.height = fival->height, | 
 | 737 | 		.which = V4L2_SUBDEV_FORMAT_ACTIVE, | 
 | 738 | 	}; | 
 | 739 | 	int ret; | 
 | 740 |  | 
 | 741 | 	isi_fmt = find_format_by_fourcc(isi, fival->pixel_format); | 
 | 742 | 	if (!isi_fmt) | 
 | 743 | 		return -EINVAL; | 
 | 744 |  | 
 | 745 | 	fie.code = isi_fmt->mbus_code; | 
 | 746 |  | 
 | 747 | 	ret = v4l2_subdev_call(isi->entity.subdev, pad, | 
 | 748 | 			       enum_frame_interval, NULL, &fie); | 
 | 749 | 	if (ret) | 
 | 750 | 		return ret; | 
 | 751 |  | 
 | 752 | 	fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; | 
 | 753 | 	fival->discrete = fie.interval; | 
 | 754 |  | 
 | 755 | 	return 0; | 
 | 756 | } | 
 | 757 |  | 
 | 758 | static void isi_camera_set_bus_param(struct atmel_isi *isi) | 
 | 759 | { | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 760 | 	u32 cfg1 = 0; | 
 | 761 |  | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 762 | 	/* set bus param for ISI */ | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 763 | 	if (isi->pdata.hsync_act_low) | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 764 | 		cfg1 |= ISI_CFG1_HSYNC_POL_ACTIVE_LOW; | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 765 | 	if (isi->pdata.vsync_act_low) | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 766 | 		cfg1 |= ISI_CFG1_VSYNC_POL_ACTIVE_LOW; | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 767 | 	if (isi->pdata.pclk_act_falling) | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 768 | 		cfg1 |= ISI_CFG1_PIXCLK_POL_ACTIVE_FALLING; | 
| Josh Wu | 833e106 | 2014-07-25 07:13:39 -0300 | [diff] [blame] | 769 | 	if (isi->pdata.has_emb_sync) | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 770 | 		cfg1 |= ISI_CFG1_EMB_SYNC; | 
| Josh Wu | 833e106 | 2014-07-25 07:13:39 -0300 | [diff] [blame] | 771 | 	if (isi->pdata.full_mode) | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 772 | 		cfg1 |= ISI_CFG1_FULL_MODE; | 
 | 773 |  | 
| Josh Wu | ce037f1 | 2014-11-25 06:30:25 -0300 | [diff] [blame] | 774 | 	cfg1 |= ISI_CFG1_THMASK_BEATS_16; | 
 | 775 |  | 
| Josh Wu | f3745a3a | 2015-05-26 06:54:46 -0300 | [diff] [blame] | 776 | 	/* Enable PM and peripheral clock before operate isi registers */ | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 777 | 	pm_runtime_get_sync(isi->dev); | 
| Josh Wu | f3745a3a | 2015-05-26 06:54:46 -0300 | [diff] [blame] | 778 |  | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 779 | 	isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); | 
 | 780 | 	isi_writel(isi, ISI_CFG1, cfg1); | 
 | 781 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 782 | 	pm_runtime_put(isi->dev); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 783 | } | 
 | 784 |  | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 785 | /* -----------------------------------------------------------------------*/ | 
| Laurent Pinchart | 40a78f3 | 2015-08-01 06:22:54 -0300 | [diff] [blame] | 786 | static int atmel_isi_parse_dt(struct atmel_isi *isi, | 
| Josh Wu | 8ff19bc | 2014-07-28 04:25:17 -0300 | [diff] [blame] | 787 | 			struct platform_device *pdev) | 
 | 788 | { | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 789 | 	struct device_node *np = pdev->dev.of_node; | 
| Sakari Ailus | 60359a2 | 2018-07-31 05:15:50 -0400 | [diff] [blame] | 790 | 	struct v4l2_fwnode_endpoint ep = { .bus_type = 0 }; | 
| Josh Wu | 8ff19bc | 2014-07-28 04:25:17 -0300 | [diff] [blame] | 791 | 	int err; | 
 | 792 |  | 
 | 793 | 	/* Default settings for ISI */ | 
 | 794 | 	isi->pdata.full_mode = 1; | 
| Josh Wu | 8ff19bc | 2014-07-28 04:25:17 -0300 | [diff] [blame] | 795 | 	isi->pdata.frate = ISI_CFG1_FRATE_CAPTURE_ALL; | 
 | 796 |  | 
 | 797 | 	np = of_graph_get_next_endpoint(np, NULL); | 
 | 798 | 	if (!np) { | 
 | 799 | 		dev_err(&pdev->dev, "Could not find the endpoint\n"); | 
 | 800 | 		return -EINVAL; | 
 | 801 | 	} | 
 | 802 |  | 
| Sakari Ailus | 859969b | 2016-08-26 20:17:25 -0300 | [diff] [blame] | 803 | 	err = v4l2_fwnode_endpoint_parse(of_fwnode_handle(np), &ep); | 
| Laurent Pinchart | 3751239 | 2015-08-01 06:22:53 -0300 | [diff] [blame] | 804 | 	of_node_put(np); | 
| Josh Wu | 8ff19bc | 2014-07-28 04:25:17 -0300 | [diff] [blame] | 805 | 	if (err) { | 
 | 806 | 		dev_err(&pdev->dev, "Could not parse the endpoint\n"); | 
| Laurent Pinchart | 3751239 | 2015-08-01 06:22:53 -0300 | [diff] [blame] | 807 | 		return err; | 
| Josh Wu | 8ff19bc | 2014-07-28 04:25:17 -0300 | [diff] [blame] | 808 | 	} | 
 | 809 |  | 
 | 810 | 	switch (ep.bus.parallel.bus_width) { | 
 | 811 | 	case 8: | 
 | 812 | 		isi->pdata.data_width_flags = ISI_DATAWIDTH_8; | 
 | 813 | 		break; | 
 | 814 | 	case 10: | 
 | 815 | 		isi->pdata.data_width_flags = | 
 | 816 | 				ISI_DATAWIDTH_8 | ISI_DATAWIDTH_10; | 
 | 817 | 		break; | 
 | 818 | 	default: | 
 | 819 | 		dev_err(&pdev->dev, "Unsupported bus width: %d\n", | 
 | 820 | 				ep.bus.parallel.bus_width); | 
| Laurent Pinchart | 3751239 | 2015-08-01 06:22:53 -0300 | [diff] [blame] | 821 | 		return -EINVAL; | 
| Josh Wu | 8ff19bc | 2014-07-28 04:25:17 -0300 | [diff] [blame] | 822 | 	} | 
 | 823 |  | 
| Josh Wu | ac4033e | 2015-08-04 06:37:49 -0300 | [diff] [blame] | 824 | 	if (ep.bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) | 
 | 825 | 		isi->pdata.hsync_act_low = true; | 
 | 826 | 	if (ep.bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) | 
 | 827 | 		isi->pdata.vsync_act_low = true; | 
 | 828 | 	if (ep.bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) | 
 | 829 | 		isi->pdata.pclk_act_falling = true; | 
 | 830 |  | 
 | 831 | 	if (ep.bus_type == V4L2_MBUS_BT656) | 
 | 832 | 		isi->pdata.has_emb_sync = true; | 
 | 833 |  | 
| Laurent Pinchart | 3751239 | 2015-08-01 06:22:53 -0300 | [diff] [blame] | 834 | 	return 0; | 
| Josh Wu | 8ff19bc | 2014-07-28 04:25:17 -0300 | [diff] [blame] | 835 | } | 
 | 836 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 837 | static int isi_open(struct file *file) | 
 | 838 | { | 
 | 839 | 	struct atmel_isi *isi = video_drvdata(file); | 
 | 840 | 	struct v4l2_subdev *sd = isi->entity.subdev; | 
 | 841 | 	int ret; | 
 | 842 |  | 
 | 843 | 	if (mutex_lock_interruptible(&isi->lock)) | 
 | 844 | 		return -ERESTARTSYS; | 
 | 845 |  | 
 | 846 | 	ret = v4l2_fh_open(file); | 
 | 847 | 	if (ret < 0) | 
 | 848 | 		goto unlock; | 
 | 849 |  | 
 | 850 | 	if (!v4l2_fh_is_singular_file(file)) | 
 | 851 | 		goto fh_rel; | 
 | 852 |  | 
 | 853 | 	ret = v4l2_subdev_call(sd, core, s_power, 1); | 
 | 854 | 	if (ret < 0 && ret != -ENOIOCTLCMD) | 
 | 855 | 		goto fh_rel; | 
 | 856 |  | 
 | 857 | 	ret = isi_set_fmt(isi, &isi->fmt); | 
 | 858 | 	if (ret) | 
 | 859 | 		v4l2_subdev_call(sd, core, s_power, 0); | 
 | 860 | fh_rel: | 
 | 861 | 	if (ret) | 
 | 862 | 		v4l2_fh_release(file); | 
 | 863 | unlock: | 
 | 864 | 	mutex_unlock(&isi->lock); | 
 | 865 | 	return ret; | 
 | 866 | } | 
 | 867 |  | 
 | 868 | static int isi_release(struct file *file) | 
 | 869 | { | 
 | 870 | 	struct atmel_isi *isi = video_drvdata(file); | 
 | 871 | 	struct v4l2_subdev *sd = isi->entity.subdev; | 
 | 872 | 	bool fh_singular; | 
 | 873 | 	int ret; | 
 | 874 |  | 
 | 875 | 	mutex_lock(&isi->lock); | 
 | 876 |  | 
 | 877 | 	fh_singular = v4l2_fh_is_singular_file(file); | 
 | 878 |  | 
 | 879 | 	ret = _vb2_fop_release(file, NULL); | 
 | 880 |  | 
 | 881 | 	if (fh_singular) | 
 | 882 | 		v4l2_subdev_call(sd, core, s_power, 0); | 
 | 883 |  | 
 | 884 | 	mutex_unlock(&isi->lock); | 
 | 885 |  | 
 | 886 | 	return ret; | 
 | 887 | } | 
 | 888 |  | 
 | 889 | static const struct v4l2_ioctl_ops isi_ioctl_ops = { | 
 | 890 | 	.vidioc_querycap		= isi_querycap, | 
 | 891 |  | 
 | 892 | 	.vidioc_try_fmt_vid_cap		= isi_try_fmt_vid_cap, | 
 | 893 | 	.vidioc_g_fmt_vid_cap		= isi_g_fmt_vid_cap, | 
 | 894 | 	.vidioc_s_fmt_vid_cap		= isi_s_fmt_vid_cap, | 
 | 895 | 	.vidioc_enum_fmt_vid_cap	= isi_enum_fmt_vid_cap, | 
 | 896 |  | 
 | 897 | 	.vidioc_enum_input		= isi_enum_input, | 
 | 898 | 	.vidioc_g_input			= isi_g_input, | 
 | 899 | 	.vidioc_s_input			= isi_s_input, | 
 | 900 |  | 
 | 901 | 	.vidioc_g_parm			= isi_g_parm, | 
 | 902 | 	.vidioc_s_parm			= isi_s_parm, | 
 | 903 | 	.vidioc_enum_framesizes		= isi_enum_framesizes, | 
 | 904 | 	.vidioc_enum_frameintervals	= isi_enum_frameintervals, | 
 | 905 |  | 
 | 906 | 	.vidioc_reqbufs			= vb2_ioctl_reqbufs, | 
 | 907 | 	.vidioc_create_bufs		= vb2_ioctl_create_bufs, | 
 | 908 | 	.vidioc_querybuf		= vb2_ioctl_querybuf, | 
 | 909 | 	.vidioc_qbuf			= vb2_ioctl_qbuf, | 
 | 910 | 	.vidioc_dqbuf			= vb2_ioctl_dqbuf, | 
 | 911 | 	.vidioc_expbuf			= vb2_ioctl_expbuf, | 
 | 912 | 	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf, | 
 | 913 | 	.vidioc_streamon		= vb2_ioctl_streamon, | 
 | 914 | 	.vidioc_streamoff		= vb2_ioctl_streamoff, | 
 | 915 |  | 
 | 916 | 	.vidioc_log_status		= v4l2_ctrl_log_status, | 
 | 917 | 	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event, | 
 | 918 | 	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe, | 
 | 919 | }; | 
 | 920 |  | 
 | 921 | static const struct v4l2_file_operations isi_fops = { | 
 | 922 | 	.owner		= THIS_MODULE, | 
 | 923 | 	.unlocked_ioctl	= video_ioctl2, | 
 | 924 | 	.open		= isi_open, | 
 | 925 | 	.release	= isi_release, | 
 | 926 | 	.poll		= vb2_fop_poll, | 
 | 927 | 	.mmap		= vb2_fop_mmap, | 
 | 928 | 	.read		= vb2_fop_read, | 
 | 929 | }; | 
 | 930 |  | 
 | 931 | static int isi_set_default_fmt(struct atmel_isi *isi) | 
 | 932 | { | 
 | 933 | 	struct v4l2_format f = { | 
 | 934 | 		.type = V4L2_BUF_TYPE_VIDEO_CAPTURE, | 
 | 935 | 		.fmt.pix = { | 
 | 936 | 			.width		= VGA_WIDTH, | 
 | 937 | 			.height		= VGA_HEIGHT, | 
 | 938 | 			.field		= V4L2_FIELD_NONE, | 
 | 939 | 			.pixelformat	= isi->user_formats[0]->fourcc, | 
 | 940 | 		}, | 
 | 941 | 	}; | 
 | 942 | 	int ret; | 
 | 943 |  | 
 | 944 | 	ret = isi_try_fmt(isi, &f, NULL); | 
 | 945 | 	if (ret) | 
 | 946 | 		return ret; | 
 | 947 | 	isi->current_fmt = isi->user_formats[0]; | 
 | 948 | 	isi->fmt = f; | 
 | 949 | 	return 0; | 
 | 950 | } | 
 | 951 |  | 
 | 952 | static const struct isi_format isi_formats[] = { | 
 | 953 | 	{ | 
 | 954 | 		.fourcc = V4L2_PIX_FMT_YUYV, | 
 | 955 | 		.mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, | 
 | 956 | 		.bpp = 2, | 
 | 957 | 		.swap = ISI_CFG2_YCC_SWAP_DEFAULT, | 
 | 958 | 	}, { | 
 | 959 | 		.fourcc = V4L2_PIX_FMT_YUYV, | 
 | 960 | 		.mbus_code = MEDIA_BUS_FMT_YVYU8_2X8, | 
 | 961 | 		.bpp = 2, | 
 | 962 | 		.swap = ISI_CFG2_YCC_SWAP_MODE_1, | 
 | 963 | 	}, { | 
 | 964 | 		.fourcc = V4L2_PIX_FMT_YUYV, | 
 | 965 | 		.mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, | 
 | 966 | 		.bpp = 2, | 
 | 967 | 		.swap = ISI_CFG2_YCC_SWAP_MODE_2, | 
 | 968 | 	}, { | 
 | 969 | 		.fourcc = V4L2_PIX_FMT_YUYV, | 
 | 970 | 		.mbus_code = MEDIA_BUS_FMT_VYUY8_2X8, | 
 | 971 | 		.bpp = 2, | 
 | 972 | 		.swap = ISI_CFG2_YCC_SWAP_MODE_3, | 
 | 973 | 	}, { | 
 | 974 | 		.fourcc = V4L2_PIX_FMT_RGB565, | 
 | 975 | 		.mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, | 
 | 976 | 		.bpp = 2, | 
 | 977 | 		.swap = ISI_CFG2_YCC_SWAP_MODE_2, | 
 | 978 | 	}, { | 
 | 979 | 		.fourcc = V4L2_PIX_FMT_RGB565, | 
 | 980 | 		.mbus_code = MEDIA_BUS_FMT_YVYU8_2X8, | 
 | 981 | 		.bpp = 2, | 
 | 982 | 		.swap = ISI_CFG2_YCC_SWAP_MODE_3, | 
 | 983 | 	}, { | 
 | 984 | 		.fourcc = V4L2_PIX_FMT_RGB565, | 
 | 985 | 		.mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, | 
 | 986 | 		.bpp = 2, | 
 | 987 | 		.swap = ISI_CFG2_YCC_SWAP_DEFAULT, | 
 | 988 | 	}, { | 
 | 989 | 		.fourcc = V4L2_PIX_FMT_RGB565, | 
 | 990 | 		.mbus_code = MEDIA_BUS_FMT_VYUY8_2X8, | 
 | 991 | 		.bpp = 2, | 
 | 992 | 		.swap = ISI_CFG2_YCC_SWAP_MODE_1, | 
 | 993 | 	}, | 
 | 994 | }; | 
 | 995 |  | 
 | 996 | static int isi_formats_init(struct atmel_isi *isi) | 
 | 997 | { | 
 | 998 | 	const struct isi_format *isi_fmts[ARRAY_SIZE(isi_formats)]; | 
 | 999 | 	unsigned int num_fmts = 0, i, j; | 
 | 1000 | 	struct v4l2_subdev *subdev = isi->entity.subdev; | 
 | 1001 | 	struct v4l2_subdev_mbus_code_enum mbus_code = { | 
 | 1002 | 		.which = V4L2_SUBDEV_FORMAT_ACTIVE, | 
 | 1003 | 	}; | 
 | 1004 |  | 
 | 1005 | 	while (!v4l2_subdev_call(subdev, pad, enum_mbus_code, | 
 | 1006 | 				 NULL, &mbus_code)) { | 
 | 1007 | 		for (i = 0; i < ARRAY_SIZE(isi_formats); i++) { | 
 | 1008 | 			if (isi_formats[i].mbus_code != mbus_code.code) | 
 | 1009 | 				continue; | 
 | 1010 |  | 
 | 1011 | 			/* Code supported, have we got this fourcc yet? */ | 
 | 1012 | 			for (j = 0; j < num_fmts; j++) | 
 | 1013 | 				if (isi_fmts[j]->fourcc == isi_formats[i].fourcc) | 
 | 1014 | 					/* Already available */ | 
 | 1015 | 					break; | 
 | 1016 | 			if (j == num_fmts) | 
 | 1017 | 				/* new */ | 
 | 1018 | 				isi_fmts[num_fmts++] = isi_formats + i; | 
 | 1019 | 		} | 
 | 1020 | 		mbus_code.index++; | 
 | 1021 | 	} | 
 | 1022 |  | 
 | 1023 | 	if (!num_fmts) | 
 | 1024 | 		return -ENXIO; | 
 | 1025 |  | 
 | 1026 | 	isi->num_user_formats = num_fmts; | 
 | 1027 | 	isi->user_formats = devm_kcalloc(isi->dev, | 
 | 1028 | 					 num_fmts, sizeof(struct isi_format *), | 
 | 1029 | 					 GFP_KERNEL); | 
| Markus Elfring | c38e865 | 2017-08-28 05:46:57 -0400 | [diff] [blame] | 1030 | 	if (!isi->user_formats) | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1031 | 		return -ENOMEM; | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1032 |  | 
 | 1033 | 	memcpy(isi->user_formats, isi_fmts, | 
 | 1034 | 	       num_fmts * sizeof(struct isi_format *)); | 
 | 1035 | 	isi->current_fmt = isi->user_formats[0]; | 
 | 1036 |  | 
 | 1037 | 	return 0; | 
 | 1038 | } | 
 | 1039 |  | 
 | 1040 | static int isi_graph_notify_complete(struct v4l2_async_notifier *notifier) | 
 | 1041 | { | 
 | 1042 | 	struct atmel_isi *isi = notifier_to_isi(notifier); | 
 | 1043 | 	int ret; | 
 | 1044 |  | 
| Hugues Fruchet | ec62c9a | 2017-05-19 07:04:52 -0300 | [diff] [blame] | 1045 | 	isi->vdev->ctrl_handler = isi->entity.subdev->ctrl_handler; | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1046 | 	ret = isi_formats_init(isi); | 
 | 1047 | 	if (ret) { | 
 | 1048 | 		dev_err(isi->dev, "No supported mediabus format found\n"); | 
 | 1049 | 		return ret; | 
 | 1050 | 	} | 
 | 1051 | 	isi_camera_set_bus_param(isi); | 
 | 1052 |  | 
 | 1053 | 	ret = isi_set_default_fmt(isi); | 
 | 1054 | 	if (ret) { | 
 | 1055 | 		dev_err(isi->dev, "Could not set default format\n"); | 
 | 1056 | 		return ret; | 
 | 1057 | 	} | 
 | 1058 |  | 
 | 1059 | 	ret = video_register_device(isi->vdev, VFL_TYPE_GRABBER, -1); | 
 | 1060 | 	if (ret) { | 
 | 1061 | 		dev_err(isi->dev, "Failed to register video device\n"); | 
 | 1062 | 		return ret; | 
 | 1063 | 	} | 
 | 1064 |  | 
 | 1065 | 	dev_dbg(isi->dev, "Device registered as %s\n", | 
 | 1066 | 		video_device_node_name(isi->vdev)); | 
 | 1067 | 	return 0; | 
 | 1068 | } | 
 | 1069 |  | 
 | 1070 | static void isi_graph_notify_unbind(struct v4l2_async_notifier *notifier, | 
 | 1071 | 				     struct v4l2_subdev *sd, | 
 | 1072 | 				     struct v4l2_async_subdev *asd) | 
 | 1073 | { | 
 | 1074 | 	struct atmel_isi *isi = notifier_to_isi(notifier); | 
 | 1075 |  | 
 | 1076 | 	dev_dbg(isi->dev, "Removing %s\n", video_device_node_name(isi->vdev)); | 
 | 1077 |  | 
| Mauro Carvalho Chehab | 8b72c18 | 2019-02-18 14:29:00 -0500 | [diff] [blame] | 1078 | 	/* Checks internally if vdev have been init or not */ | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1079 | 	video_unregister_device(isi->vdev); | 
 | 1080 | } | 
 | 1081 |  | 
 | 1082 | static int isi_graph_notify_bound(struct v4l2_async_notifier *notifier, | 
 | 1083 | 				   struct v4l2_subdev *subdev, | 
 | 1084 | 				   struct v4l2_async_subdev *asd) | 
 | 1085 | { | 
 | 1086 | 	struct atmel_isi *isi = notifier_to_isi(notifier); | 
 | 1087 |  | 
 | 1088 | 	dev_dbg(isi->dev, "subdev %s bound\n", subdev->name); | 
 | 1089 |  | 
 | 1090 | 	isi->entity.subdev = subdev; | 
 | 1091 |  | 
 | 1092 | 	return 0; | 
 | 1093 | } | 
 | 1094 |  | 
| Laurent Pinchart | b6ee3f0 | 2017-08-30 13:18:04 -0400 | [diff] [blame] | 1095 | static const struct v4l2_async_notifier_operations isi_graph_notify_ops = { | 
 | 1096 | 	.bound = isi_graph_notify_bound, | 
 | 1097 | 	.unbind = isi_graph_notify_unbind, | 
 | 1098 | 	.complete = isi_graph_notify_complete, | 
 | 1099 | }; | 
 | 1100 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1101 | static int isi_graph_parse(struct atmel_isi *isi, struct device_node *node) | 
 | 1102 | { | 
 | 1103 | 	struct device_node *ep = NULL; | 
 | 1104 | 	struct device_node *remote; | 
 | 1105 |  | 
| Nicholas Mc Guire | e8ced20 | 2018-06-01 08:46:14 -0400 | [diff] [blame] | 1106 | 	ep = of_graph_get_next_endpoint(node, ep); | 
 | 1107 | 	if (!ep) | 
 | 1108 | 		return -EINVAL; | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1109 |  | 
| Nicholas Mc Guire | e8ced20 | 2018-06-01 08:46:14 -0400 | [diff] [blame] | 1110 | 	remote = of_graph_get_remote_port_parent(ep); | 
| Nicholas Mc Guire | 4f9195e | 2018-06-01 09:30:14 -0400 | [diff] [blame] | 1111 | 	of_node_put(ep); | 
 | 1112 | 	if (!remote) | 
| Nicholas Mc Guire | e8ced20 | 2018-06-01 08:46:14 -0400 | [diff] [blame] | 1113 | 		return -EINVAL; | 
| Nicholas Mc Guire | e8ced20 | 2018-06-01 08:46:14 -0400 | [diff] [blame] | 1114 |  | 
 | 1115 | 	/* Remote node to connect */ | 
 | 1116 | 	isi->entity.node = remote; | 
 | 1117 | 	isi->entity.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; | 
 | 1118 | 	isi->entity.asd.match.fwnode = of_fwnode_handle(remote); | 
 | 1119 | 	return 0; | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1120 | } | 
 | 1121 |  | 
 | 1122 | static int isi_graph_init(struct atmel_isi *isi) | 
 | 1123 | { | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1124 | 	int ret; | 
 | 1125 |  | 
 | 1126 | 	/* Parse the graph to extract a list of subdevice DT nodes. */ | 
 | 1127 | 	ret = isi_graph_parse(isi, isi->dev->of_node); | 
 | 1128 | 	if (ret < 0) { | 
 | 1129 | 		dev_err(isi->dev, "Graph parsing failed\n"); | 
 | 1130 | 		return ret; | 
 | 1131 | 	} | 
 | 1132 |  | 
| Steve Longerbeam | d079f94 | 2018-09-29 15:54:18 -0400 | [diff] [blame] | 1133 | 	v4l2_async_notifier_init(&isi->notifier); | 
 | 1134 |  | 
 | 1135 | 	ret = v4l2_async_notifier_add_subdev(&isi->notifier, &isi->entity.asd); | 
 | 1136 | 	if (ret) { | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1137 | 		of_node_put(isi->entity.node); | 
| Steve Longerbeam | d079f94 | 2018-09-29 15:54:18 -0400 | [diff] [blame] | 1138 | 		return ret; | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1139 | 	} | 
 | 1140 |  | 
| Laurent Pinchart | b6ee3f0 | 2017-08-30 13:18:04 -0400 | [diff] [blame] | 1141 | 	isi->notifier.ops = &isi_graph_notify_ops; | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1142 |  | 
 | 1143 | 	ret = v4l2_async_notifier_register(&isi->v4l2_dev, &isi->notifier); | 
 | 1144 | 	if (ret < 0) { | 
 | 1145 | 		dev_err(isi->dev, "Notifier registration failed\n"); | 
| Steve Longerbeam | d079f94 | 2018-09-29 15:54:18 -0400 | [diff] [blame] | 1146 | 		v4l2_async_notifier_cleanup(&isi->notifier); | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1147 | 		return ret; | 
 | 1148 | 	} | 
 | 1149 |  | 
 | 1150 | 	return 0; | 
 | 1151 | } | 
 | 1152 |  | 
 | 1153 |  | 
| Greg Kroah-Hartman | 4c62e97 | 2012-12-21 13:17:53 -0800 | [diff] [blame] | 1154 | static int atmel_isi_probe(struct platform_device *pdev) | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1155 | { | 
| Guenter Roeck | 0724745f | 2016-02-09 12:43:42 -0200 | [diff] [blame] | 1156 | 	int irq; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1157 | 	struct atmel_isi *isi; | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1158 | 	struct vb2_queue *q; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1159 | 	struct resource *regs; | 
 | 1160 | 	int ret, i; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1161 |  | 
| Laurent Pinchart | c52c0cb | 2013-11-25 12:13:50 -0300 | [diff] [blame] | 1162 | 	isi = devm_kzalloc(&pdev->dev, sizeof(struct atmel_isi), GFP_KERNEL); | 
| Markus Elfring | c38e865 | 2017-08-28 05:46:57 -0400 | [diff] [blame] | 1163 | 	if (!isi) | 
| Laurent Pinchart | c52c0cb | 2013-11-25 12:13:50 -0300 | [diff] [blame] | 1164 | 		return -ENOMEM; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1165 |  | 
| Laurent Pinchart | c52c0cb | 2013-11-25 12:13:50 -0300 | [diff] [blame] | 1166 | 	isi->pclk = devm_clk_get(&pdev->dev, "isi_clk"); | 
 | 1167 | 	if (IS_ERR(isi->pclk)) | 
 | 1168 | 		return PTR_ERR(isi->pclk); | 
 | 1169 |  | 
| Laurent Pinchart | 40a78f3 | 2015-08-01 06:22:54 -0300 | [diff] [blame] | 1170 | 	ret = atmel_isi_parse_dt(isi, pdev); | 
 | 1171 | 	if (ret) | 
 | 1172 | 		return ret; | 
| Josh Wu | 8ff19bc | 2014-07-28 04:25:17 -0300 | [diff] [blame] | 1173 |  | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1174 | 	isi->active = NULL; | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1175 | 	isi->dev = &pdev->dev; | 
 | 1176 | 	mutex_init(&isi->lock); | 
 | 1177 | 	spin_lock_init(&isi->irqlock); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1178 | 	INIT_LIST_HEAD(&isi->video_buffer_list); | 
 | 1179 | 	INIT_LIST_HEAD(&isi->dma_desc_head); | 
 | 1180 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1181 | 	q = &isi->queue; | 
 | 1182 |  | 
 | 1183 | 	/* Initialize the top-level structure */ | 
 | 1184 | 	ret = v4l2_device_register(&pdev->dev, &isi->v4l2_dev); | 
 | 1185 | 	if (ret) | 
 | 1186 | 		return ret; | 
 | 1187 |  | 
 | 1188 | 	isi->vdev = video_device_alloc(); | 
| Markus Elfring | af28c99 | 2017-08-28 06:50:28 -0400 | [diff] [blame] | 1189 | 	if (!isi->vdev) { | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1190 | 		ret = -ENOMEM; | 
 | 1191 | 		goto err_vdev_alloc; | 
 | 1192 | 	} | 
 | 1193 |  | 
 | 1194 | 	/* video node */ | 
 | 1195 | 	isi->vdev->fops = &isi_fops; | 
 | 1196 | 	isi->vdev->v4l2_dev = &isi->v4l2_dev; | 
 | 1197 | 	isi->vdev->queue = &isi->queue; | 
| Mauro Carvalho Chehab | c0decac | 2018-09-10 08:19:14 -0400 | [diff] [blame] | 1198 | 	strscpy(isi->vdev->name, KBUILD_MODNAME, sizeof(isi->vdev->name)); | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1199 | 	isi->vdev->release = video_device_release; | 
 | 1200 | 	isi->vdev->ioctl_ops = &isi_ioctl_ops; | 
 | 1201 | 	isi->vdev->lock = &isi->lock; | 
 | 1202 | 	isi->vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | | 
 | 1203 | 		V4L2_CAP_READWRITE; | 
 | 1204 | 	video_set_drvdata(isi->vdev, isi); | 
 | 1205 |  | 
 | 1206 | 	/* buffer queue */ | 
 | 1207 | 	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | 
 | 1208 | 	q->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; | 
 | 1209 | 	q->lock = &isi->lock; | 
 | 1210 | 	q->drv_priv = isi; | 
 | 1211 | 	q->buf_struct_size = sizeof(struct frame_buffer); | 
 | 1212 | 	q->ops = &isi_video_qops; | 
 | 1213 | 	q->mem_ops = &vb2_dma_contig_memops; | 
 | 1214 | 	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; | 
 | 1215 | 	q->min_buffers_needed = 2; | 
 | 1216 | 	q->dev = &pdev->dev; | 
 | 1217 |  | 
 | 1218 | 	ret = vb2_queue_init(q); | 
 | 1219 | 	if (ret < 0) { | 
 | 1220 | 		dev_err(&pdev->dev, "failed to initialize VB2 queue\n"); | 
 | 1221 | 		goto err_vb2_queue; | 
 | 1222 | 	} | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1223 | 	isi->p_fb_descriptors = dma_alloc_coherent(&pdev->dev, | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1224 | 				sizeof(struct fbd) * VIDEO_MAX_FRAME, | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1225 | 				&isi->fb_descriptors_phys, | 
 | 1226 | 				GFP_KERNEL); | 
 | 1227 | 	if (!isi->p_fb_descriptors) { | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1228 | 		dev_err(&pdev->dev, "Can't allocate descriptors!\n"); | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1229 | 		ret = -ENOMEM; | 
 | 1230 | 		goto err_dma_alloc; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1231 | 	} | 
 | 1232 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1233 | 	for (i = 0; i < VIDEO_MAX_FRAME; i++) { | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1234 | 		isi->dma_desc[i].p_fbd = isi->p_fb_descriptors + i; | 
 | 1235 | 		isi->dma_desc[i].fbd_phys = isi->fb_descriptors_phys + | 
 | 1236 | 					i * sizeof(struct fbd); | 
 | 1237 | 		list_add(&isi->dma_desc[i].list, &isi->dma_desc_head); | 
 | 1238 | 	} | 
 | 1239 |  | 
| Laurent Pinchart | c52c0cb | 2013-11-25 12:13:50 -0300 | [diff] [blame] | 1240 | 	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
 | 1241 | 	isi->regs = devm_ioremap_resource(&pdev->dev, regs); | 
 | 1242 | 	if (IS_ERR(isi->regs)) { | 
 | 1243 | 		ret = PTR_ERR(isi->regs); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1244 | 		goto err_ioremap; | 
 | 1245 | 	} | 
 | 1246 |  | 
| Josh Wu | 833e106 | 2014-07-25 07:13:39 -0300 | [diff] [blame] | 1247 | 	if (isi->pdata.data_width_flags & ISI_DATAWIDTH_8) | 
| Guennadi Liakhovetski | a4e9f10 | 2011-07-27 12:18:37 -0300 | [diff] [blame] | 1248 | 		isi->width_flags = 1 << 7; | 
| Josh Wu | 833e106 | 2014-07-25 07:13:39 -0300 | [diff] [blame] | 1249 | 	if (isi->pdata.data_width_flags & ISI_DATAWIDTH_10) | 
| Guennadi Liakhovetski | a4e9f10 | 2011-07-27 12:18:37 -0300 | [diff] [blame] | 1250 | 		isi->width_flags |= 1 << 9; | 
 | 1251 |  | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1252 | 	irq = platform_get_irq(pdev, 0); | 
| Guenter Roeck | 0724745f | 2016-02-09 12:43:42 -0200 | [diff] [blame] | 1253 | 	if (irq < 0) { | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1254 | 		ret = irq; | 
 | 1255 | 		goto err_req_irq; | 
 | 1256 | 	} | 
 | 1257 |  | 
| Laurent Pinchart | c52c0cb | 2013-11-25 12:13:50 -0300 | [diff] [blame] | 1258 | 	ret = devm_request_irq(&pdev->dev, irq, isi_interrupt, 0, "isi", isi); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1259 | 	if (ret) { | 
 | 1260 | 		dev_err(&pdev->dev, "Unable to request irq %d\n", irq); | 
 | 1261 | 		goto err_req_irq; | 
 | 1262 | 	} | 
 | 1263 | 	isi->irq = irq; | 
 | 1264 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1265 | 	ret = isi_graph_init(isi); | 
 | 1266 | 	if (ret < 0) | 
 | 1267 | 		goto err_req_irq; | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1268 |  | 
| Josh Wu | f3745a3a | 2015-05-26 06:54:46 -0300 | [diff] [blame] | 1269 | 	pm_suspend_ignore_children(&pdev->dev, true); | 
 | 1270 | 	pm_runtime_enable(&pdev->dev); | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1271 | 	platform_set_drvdata(pdev, isi); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1272 | 	return 0; | 
 | 1273 |  | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1274 | err_req_irq: | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1275 | err_ioremap: | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1276 | 	dma_free_coherent(&pdev->dev, | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1277 | 			sizeof(struct fbd) * VIDEO_MAX_FRAME, | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1278 | 			isi->p_fb_descriptors, | 
 | 1279 | 			isi->fb_descriptors_phys); | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1280 | err_dma_alloc: | 
 | 1281 | err_vb2_queue: | 
 | 1282 | 	video_device_release(isi->vdev); | 
 | 1283 | err_vdev_alloc: | 
 | 1284 | 	v4l2_device_unregister(&isi->v4l2_dev); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1285 |  | 
 | 1286 | 	return ret; | 
 | 1287 | } | 
 | 1288 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1289 | static int atmel_isi_remove(struct platform_device *pdev) | 
 | 1290 | { | 
 | 1291 | 	struct atmel_isi *isi = platform_get_drvdata(pdev); | 
 | 1292 |  | 
 | 1293 | 	dma_free_coherent(&pdev->dev, | 
 | 1294 | 			sizeof(struct fbd) * VIDEO_MAX_FRAME, | 
 | 1295 | 			isi->p_fb_descriptors, | 
 | 1296 | 			isi->fb_descriptors_phys); | 
 | 1297 | 	pm_runtime_disable(&pdev->dev); | 
 | 1298 | 	v4l2_async_notifier_unregister(&isi->notifier); | 
| Steve Longerbeam | d079f94 | 2018-09-29 15:54:18 -0400 | [diff] [blame] | 1299 | 	v4l2_async_notifier_cleanup(&isi->notifier); | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1300 | 	v4l2_device_unregister(&isi->v4l2_dev); | 
 | 1301 |  | 
 | 1302 | 	return 0; | 
 | 1303 | } | 
 | 1304 |  | 
| Geert Uytterhoeven | 18baba6 | 2015-09-06 07:08:49 -0300 | [diff] [blame] | 1305 | #ifdef CONFIG_PM | 
| Josh Wu | f3745a3a | 2015-05-26 06:54:46 -0300 | [diff] [blame] | 1306 | static int atmel_isi_runtime_suspend(struct device *dev) | 
 | 1307 | { | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1308 | 	struct atmel_isi *isi = dev_get_drvdata(dev); | 
| Josh Wu | f3745a3a | 2015-05-26 06:54:46 -0300 | [diff] [blame] | 1309 |  | 
 | 1310 | 	clk_disable_unprepare(isi->pclk); | 
 | 1311 |  | 
 | 1312 | 	return 0; | 
 | 1313 | } | 
 | 1314 | static int atmel_isi_runtime_resume(struct device *dev) | 
 | 1315 | { | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1316 | 	struct atmel_isi *isi = dev_get_drvdata(dev); | 
| Josh Wu | f3745a3a | 2015-05-26 06:54:46 -0300 | [diff] [blame] | 1317 |  | 
 | 1318 | 	return clk_prepare_enable(isi->pclk); | 
 | 1319 | } | 
| Geert Uytterhoeven | 18baba6 | 2015-09-06 07:08:49 -0300 | [diff] [blame] | 1320 | #endif /* CONFIG_PM */ | 
| Josh Wu | f3745a3a | 2015-05-26 06:54:46 -0300 | [diff] [blame] | 1321 |  | 
 | 1322 | static const struct dev_pm_ops atmel_isi_dev_pm_ops = { | 
 | 1323 | 	SET_RUNTIME_PM_OPS(atmel_isi_runtime_suspend, | 
 | 1324 | 				atmel_isi_runtime_resume, NULL) | 
 | 1325 | }; | 
 | 1326 |  | 
| Josh Wu | 8ff19bc | 2014-07-28 04:25:17 -0300 | [diff] [blame] | 1327 | static const struct of_device_id atmel_isi_of_match[] = { | 
 | 1328 | 	{ .compatible = "atmel,at91sam9g45-isi" }, | 
 | 1329 | 	{ } | 
 | 1330 | }; | 
 | 1331 | MODULE_DEVICE_TABLE(of, atmel_isi_of_match); | 
 | 1332 |  | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1333 | static struct platform_driver atmel_isi_driver = { | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1334 | 	.driver		= { | 
 | 1335 | 		.name = "atmel_isi", | 
| Josh Wu | 8ff19bc | 2014-07-28 04:25:17 -0300 | [diff] [blame] | 1336 | 		.of_match_table = of_match_ptr(atmel_isi_of_match), | 
| Josh Wu | f3745a3a | 2015-05-26 06:54:46 -0300 | [diff] [blame] | 1337 | 		.pm	= &atmel_isi_dev_pm_ops, | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1338 | 	}, | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1339 | 	.probe		= atmel_isi_probe, | 
 | 1340 | 	.remove		= atmel_isi_remove, | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1341 | }; | 
 | 1342 |  | 
| Hans Verkuil | d12c908 | 2016-08-16 16:56:43 -0300 | [diff] [blame] | 1343 | module_platform_driver(atmel_isi_driver); | 
| Josh Wu | 195ebc4 | 2011-06-07 22:40:19 -0300 | [diff] [blame] | 1344 |  | 
 | 1345 | MODULE_AUTHOR("Josh Wu <josh.wu@atmel.com>"); | 
 | 1346 | MODULE_DESCRIPTION("The V4L2 driver for Atmel Linux"); | 
 | 1347 | MODULE_LICENSE("GPL"); | 
 | 1348 | MODULE_SUPPORTED_DEVICE("video"); |