blob: 985a7508af3a85cb8827f4ddda8e2d4421c73f7d [file] [log] [blame]
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001/*
2 * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
3 * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15#include <linux/module.h>
16#include <linux/export.h>
17#include <linux/types.h>
Philipp Zabel6c641552013-03-28 17:35:21 +010018#include <linux/reset.h>
Sascha Haueraecfbdb2012-09-21 10:07:49 +020019#include <linux/platform_device.h>
20#include <linux/err.h>
21#include <linux/spinlock.h>
22#include <linux/delay.h>
23#include <linux/interrupt.h>
24#include <linux/io.h>
25#include <linux/clk.h>
26#include <linux/list.h>
27#include <linux/irq.h>
Catalin Marinasde88cbb2013-01-18 15:31:37 +000028#include <linux/irqchip/chained_irq.h>
Philipp Zabelb7287662013-06-21 10:27:39 +020029#include <linux/irqdomain.h>
Sascha Haueraecfbdb2012-09-21 10:07:49 +020030#include <linux/of_device.h>
Sascha Haueraecfbdb2012-09-21 10:07:49 +020031
Philipp Zabel7cb17792013-10-10 16:18:38 +020032#include <drm/drm_fourcc.h>
33
Philipp Zabel39b90042013-09-30 16:13:39 +020034#include <video/imx-ipu-v3.h>
Sascha Haueraecfbdb2012-09-21 10:07:49 +020035#include "ipu-prv.h"
36
37static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset)
38{
39 return readl(ipu->cm_reg + offset);
40}
41
42static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset)
43{
44 writel(value, ipu->cm_reg + offset);
45}
46
Sascha Haueraecfbdb2012-09-21 10:07:49 +020047void ipu_srm_dp_sync_update(struct ipu_soc *ipu)
48{
49 u32 val;
50
51 val = ipu_cm_read(ipu, IPU_SRM_PRI2);
52 val |= 0x8;
53 ipu_cm_write(ipu, val, IPU_SRM_PRI2);
54}
55EXPORT_SYMBOL_GPL(ipu_srm_dp_sync_update);
56
Philipp Zabel7cb17792013-10-10 16:18:38 +020057enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc)
58{
59 switch (drm_fourcc) {
60 case DRM_FORMAT_RGB565:
61 case DRM_FORMAT_BGR565:
62 case DRM_FORMAT_RGB888:
63 case DRM_FORMAT_BGR888:
64 case DRM_FORMAT_XRGB8888:
65 case DRM_FORMAT_XBGR8888:
66 case DRM_FORMAT_RGBX8888:
67 case DRM_FORMAT_BGRX8888:
68 case DRM_FORMAT_ARGB8888:
69 case DRM_FORMAT_ABGR8888:
70 case DRM_FORMAT_RGBA8888:
71 case DRM_FORMAT_BGRA8888:
72 return IPUV3_COLORSPACE_RGB;
73 case DRM_FORMAT_YUYV:
74 case DRM_FORMAT_UYVY:
75 case DRM_FORMAT_YUV420:
76 case DRM_FORMAT_YVU420:
77 return IPUV3_COLORSPACE_YUV;
78 default:
79 return IPUV3_COLORSPACE_UNKNOWN;
80 }
81}
82EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace);
83
Sascha Haueraecfbdb2012-09-21 10:07:49 +020084enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat)
85{
86 switch (pixelformat) {
87 case V4L2_PIX_FMT_YUV420:
Philipp Zabeld3e4e612012-11-12 16:29:00 +010088 case V4L2_PIX_FMT_YVU420:
Sascha Haueraecfbdb2012-09-21 10:07:49 +020089 case V4L2_PIX_FMT_UYVY:
Michael Olbrichc096ae12012-11-12 16:28:59 +010090 case V4L2_PIX_FMT_YUYV:
Sascha Haueraecfbdb2012-09-21 10:07:49 +020091 return IPUV3_COLORSPACE_YUV;
92 case V4L2_PIX_FMT_RGB32:
93 case V4L2_PIX_FMT_BGR32:
94 case V4L2_PIX_FMT_RGB24:
95 case V4L2_PIX_FMT_BGR24:
96 case V4L2_PIX_FMT_RGB565:
97 return IPUV3_COLORSPACE_RGB;
98 default:
99 return IPUV3_COLORSPACE_UNKNOWN;
100 }
101}
102EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace);
103
Steve Longerbeam4cea9402014-06-25 18:05:38 -0700104bool ipu_pixelformat_is_planar(u32 pixelformat)
105{
106 switch (pixelformat) {
107 case V4L2_PIX_FMT_YUV420:
108 case V4L2_PIX_FMT_YVU420:
109 return true;
110 }
111
112 return false;
113}
114EXPORT_SYMBOL_GPL(ipu_pixelformat_is_planar);
115
Steve Longerbeamae0e9702014-06-25 18:05:36 -0700116enum ipu_color_space ipu_mbus_code_to_colorspace(u32 mbus_code)
117{
118 switch (mbus_code & 0xf000) {
119 case 0x1000:
120 return IPUV3_COLORSPACE_RGB;
121 case 0x2000:
122 return IPUV3_COLORSPACE_YUV;
123 default:
124 return IPUV3_COLORSPACE_UNKNOWN;
125 }
126}
127EXPORT_SYMBOL_GPL(ipu_mbus_code_to_colorspace);
128
Steve Longerbeamf835f382014-06-25 18:05:37 -0700129int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees,
130 bool hflip, bool vflip)
131{
132 u32 r90, vf, hf;
133
134 switch (degrees) {
135 case 0:
136 vf = hf = r90 = 0;
137 break;
138 case 90:
139 vf = hf = 0;
140 r90 = 1;
141 break;
142 case 180:
143 vf = hf = 1;
144 r90 = 0;
145 break;
146 case 270:
147 vf = hf = r90 = 1;
148 break;
149 default:
150 return -EINVAL;
151 }
152
153 hf ^= (u32)hflip;
154 vf ^= (u32)vflip;
155
156 *mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf);
157 return 0;
158}
159EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode);
160
161int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode,
162 bool hflip, bool vflip)
163{
164 u32 r90, vf, hf;
165
166 r90 = ((u32)mode >> 2) & 0x1;
167 hf = ((u32)mode >> 1) & 0x1;
168 vf = ((u32)mode >> 0) & 0x1;
169 hf ^= (u32)hflip;
170 vf ^= (u32)vflip;
171
172 switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) {
173 case IPU_ROTATE_NONE:
174 *degrees = 0;
175 break;
176 case IPU_ROTATE_90_RIGHT:
177 *degrees = 90;
178 break;
179 case IPU_ROTATE_180:
180 *degrees = 180;
181 break;
182 case IPU_ROTATE_90_LEFT:
183 *degrees = 270;
184 break;
185 default:
186 return -EINVAL;
187 }
188
189 return 0;
190}
191EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees);
192
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200193struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num)
194{
195 struct ipuv3_channel *channel;
196
197 dev_dbg(ipu->dev, "%s %d\n", __func__, num);
198
199 if (num > 63)
200 return ERR_PTR(-ENODEV);
201
202 mutex_lock(&ipu->channel_lock);
203
204 channel = &ipu->channel[num];
205
206 if (channel->busy) {
207 channel = ERR_PTR(-EBUSY);
208 goto out;
209 }
210
Valentina Manea89bc5be2013-10-25 11:52:20 +0300211 channel->busy = true;
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200212 channel->num = num;
213
214out:
215 mutex_unlock(&ipu->channel_lock);
216
217 return channel;
218}
219EXPORT_SYMBOL_GPL(ipu_idmac_get);
220
221void ipu_idmac_put(struct ipuv3_channel *channel)
222{
223 struct ipu_soc *ipu = channel->ipu;
224
225 dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num);
226
227 mutex_lock(&ipu->channel_lock);
228
Valentina Manea89bc5be2013-10-25 11:52:20 +0300229 channel->busy = false;
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200230
231 mutex_unlock(&ipu->channel_lock);
232}
233EXPORT_SYMBOL_GPL(ipu_idmac_put);
234
Steve Longerbeamaa52f572014-06-25 18:05:40 -0700235#define idma_mask(ch) (1 << ((ch) & 0x1f))
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200236
Steve Longerbeame7268c62014-06-25 18:05:42 -0700237/*
238 * This is an undocumented feature, a write one to a channel bit in
239 * IPU_CHA_CUR_BUF and IPU_CHA_TRIPLE_CUR_BUF will reset the channel's
240 * internal current buffer pointer so that transfers start from buffer
241 * 0 on the next channel enable (that's the theory anyway, the imx6 TRM
242 * only says these are read-only registers). This operation is required
243 * for channel linking to work correctly, for instance video capture
244 * pipelines that carry out image rotations will fail after the first
245 * streaming unless this function is called for each channel before
246 * re-enabling the channels.
247 */
248static void __ipu_idmac_reset_current_buffer(struct ipuv3_channel *channel)
249{
250 struct ipu_soc *ipu = channel->ipu;
251 unsigned int chno = channel->num;
252
253 ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_CUR_BUF(chno));
254}
255
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200256void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
257 bool doublebuffer)
258{
259 struct ipu_soc *ipu = channel->ipu;
260 unsigned long flags;
261 u32 reg;
262
263 spin_lock_irqsave(&ipu->lock, flags);
264
265 reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
266 if (doublebuffer)
267 reg |= idma_mask(channel->num);
268 else
269 reg &= ~idma_mask(channel->num);
270 ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num));
271
Steve Longerbeame7268c62014-06-25 18:05:42 -0700272 __ipu_idmac_reset_current_buffer(channel);
273
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200274 spin_unlock_irqrestore(&ipu->lock, flags);
275}
276EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer);
277
278int ipu_module_enable(struct ipu_soc *ipu, u32 mask)
279{
280 unsigned long lock_flags;
281 u32 val;
282
283 spin_lock_irqsave(&ipu->lock, lock_flags);
284
285 val = ipu_cm_read(ipu, IPU_DISP_GEN);
286
287 if (mask & IPU_CONF_DI0_EN)
288 val |= IPU_DI0_COUNTER_RELEASE;
289 if (mask & IPU_CONF_DI1_EN)
290 val |= IPU_DI1_COUNTER_RELEASE;
291
292 ipu_cm_write(ipu, val, IPU_DISP_GEN);
293
294 val = ipu_cm_read(ipu, IPU_CONF);
295 val |= mask;
296 ipu_cm_write(ipu, val, IPU_CONF);
297
298 spin_unlock_irqrestore(&ipu->lock, lock_flags);
299
300 return 0;
301}
302EXPORT_SYMBOL_GPL(ipu_module_enable);
303
304int ipu_module_disable(struct ipu_soc *ipu, u32 mask)
305{
306 unsigned long lock_flags;
307 u32 val;
308
309 spin_lock_irqsave(&ipu->lock, lock_flags);
310
311 val = ipu_cm_read(ipu, IPU_CONF);
312 val &= ~mask;
313 ipu_cm_write(ipu, val, IPU_CONF);
314
315 val = ipu_cm_read(ipu, IPU_DISP_GEN);
316
317 if (mask & IPU_CONF_DI0_EN)
318 val &= ~IPU_DI0_COUNTER_RELEASE;
319 if (mask & IPU_CONF_DI1_EN)
320 val &= ~IPU_DI1_COUNTER_RELEASE;
321
322 ipu_cm_write(ipu, val, IPU_DISP_GEN);
323
324 spin_unlock_irqrestore(&ipu->lock, lock_flags);
325
326 return 0;
327}
328EXPORT_SYMBOL_GPL(ipu_module_disable);
329
Philipp Zabele9046092012-05-16 17:28:29 +0200330int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel)
331{
332 struct ipu_soc *ipu = channel->ipu;
333 unsigned int chno = channel->num;
334
335 return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0;
336}
337EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer);
338
Steve Longerbeamaa52f572014-06-25 18:05:40 -0700339bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num)
340{
341 struct ipu_soc *ipu = channel->ipu;
342 unsigned long flags;
343 u32 reg = 0;
344
345 spin_lock_irqsave(&ipu->lock, flags);
346 switch (buf_num) {
347 case 0:
348 reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num));
349 break;
350 case 1:
351 reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num));
352 break;
353 case 2:
354 reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num));
355 break;
356 }
357 spin_unlock_irqrestore(&ipu->lock, flags);
358
359 return ((reg & idma_mask(channel->num)) != 0);
360}
361EXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready);
362
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200363void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num)
364{
365 struct ipu_soc *ipu = channel->ipu;
366 unsigned int chno = channel->num;
367 unsigned long flags;
368
369 spin_lock_irqsave(&ipu->lock, flags);
370
371 /* Mark buffer as ready. */
372 if (buf_num == 0)
373 ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
374 else
375 ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
376
377 spin_unlock_irqrestore(&ipu->lock, flags);
378}
379EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer);
380
Steve Longerbeambce6f082014-06-25 18:05:41 -0700381void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num)
382{
383 struct ipu_soc *ipu = channel->ipu;
384 unsigned int chno = channel->num;
385 unsigned long flags;
386
387 spin_lock_irqsave(&ipu->lock, flags);
388
389 ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */
390 switch (buf_num) {
391 case 0:
392 ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
393 break;
394 case 1:
395 ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
396 break;
397 case 2:
398 ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno));
399 break;
400 default:
401 break;
402 }
403 ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
404
405 spin_unlock_irqrestore(&ipu->lock, flags);
406}
407EXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer);
408
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200409int ipu_idmac_enable_channel(struct ipuv3_channel *channel)
410{
411 struct ipu_soc *ipu = channel->ipu;
412 u32 val;
413 unsigned long flags;
414
415 spin_lock_irqsave(&ipu->lock, flags);
416
417 val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
418 val |= idma_mask(channel->num);
419 ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
420
421 spin_unlock_irqrestore(&ipu->lock, flags);
422
423 return 0;
424}
425EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel);
426
Philipp Zabel17075502014-04-14 23:53:17 +0200427bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno)
428{
429 return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno));
430}
431EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy);
432
Sascha Hauerfb822a32013-10-10 16:18:41 +0200433int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms)
434{
435 struct ipu_soc *ipu = channel->ipu;
436 unsigned long timeout;
437
438 timeout = jiffies + msecs_to_jiffies(ms);
439 while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) &
440 idma_mask(channel->num)) {
441 if (time_after(jiffies, timeout))
442 return -ETIMEDOUT;
443 cpu_relax();
444 }
445
446 return 0;
447}
448EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy);
449
Philipp Zabel17075502014-04-14 23:53:17 +0200450int ipu_wait_interrupt(struct ipu_soc *ipu, int irq, int ms)
451{
452 unsigned long timeout;
453
454 timeout = jiffies + msecs_to_jiffies(ms);
455 ipu_cm_write(ipu, BIT(irq % 32), IPU_INT_STAT(irq / 32));
456 while (!(ipu_cm_read(ipu, IPU_INT_STAT(irq / 32) & BIT(irq % 32)))) {
457 if (time_after(jiffies, timeout))
458 return -ETIMEDOUT;
459 cpu_relax();
460 }
461
462 return 0;
463}
464EXPORT_SYMBOL_GPL(ipu_wait_interrupt);
465
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200466int ipu_idmac_disable_channel(struct ipuv3_channel *channel)
467{
468 struct ipu_soc *ipu = channel->ipu;
469 u32 val;
470 unsigned long flags;
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200471
472 spin_lock_irqsave(&ipu->lock, flags);
473
474 /* Disable DMA channel(s) */
475 val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
476 val &= ~idma_mask(channel->num);
477 ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
478
Steve Longerbeame7268c62014-06-25 18:05:42 -0700479 __ipu_idmac_reset_current_buffer(channel);
480
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200481 /* Set channel buffers NOT to be ready */
482 ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */
483
484 if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) &
485 idma_mask(channel->num)) {
486 ipu_cm_write(ipu, idma_mask(channel->num),
487 IPU_CHA_BUF0_RDY(channel->num));
488 }
489
490 if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) &
491 idma_mask(channel->num)) {
492 ipu_cm_write(ipu, idma_mask(channel->num),
493 IPU_CHA_BUF1_RDY(channel->num));
494 }
495
496 ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
497
498 /* Reset the double buffer */
499 val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
500 val &= ~idma_mask(channel->num);
501 ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num));
502
503 spin_unlock_irqrestore(&ipu->lock, flags);
504
505 return 0;
506}
507EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel);
508
Philipp Zabel6c641552013-03-28 17:35:21 +0100509static int ipu_memory_reset(struct ipu_soc *ipu)
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200510{
511 unsigned long timeout;
512
513 ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST);
514
515 timeout = jiffies + msecs_to_jiffies(1000);
516 while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) {
517 if (time_after(jiffies, timeout))
518 return -ETIME;
519 cpu_relax();
520 }
521
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200522 return 0;
523}
524
Steve Longerbeamba079752014-06-25 18:05:30 -0700525/*
526 * Set the source mux for the given CSI. Selects either parallel or
527 * MIPI CSI2 sources.
528 */
529void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2)
530{
531 unsigned long flags;
532 u32 val, mask;
533
534 mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE :
535 IPU_CONF_CSI0_DATA_SOURCE;
536
537 spin_lock_irqsave(&ipu->lock, flags);
538
539 val = ipu_cm_read(ipu, IPU_CONF);
540 if (mipi_csi2)
541 val |= mask;
542 else
543 val &= ~mask;
544 ipu_cm_write(ipu, val, IPU_CONF);
545
546 spin_unlock_irqrestore(&ipu->lock, flags);
547}
548EXPORT_SYMBOL_GPL(ipu_set_csi_src_mux);
549
550/*
551 * Set the source mux for the IC. Selects either CSI[01] or the VDI.
552 */
553void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
554{
555 unsigned long flags;
556 u32 val;
557
558 spin_lock_irqsave(&ipu->lock, flags);
559
560 val = ipu_cm_read(ipu, IPU_CONF);
561 if (vdi) {
562 val |= IPU_CONF_IC_INPUT;
563 } else {
564 val &= ~IPU_CONF_IC_INPUT;
565 if (csi_id == 1)
566 val |= IPU_CONF_CSI_SEL;
567 else
568 val &= ~IPU_CONF_CSI_SEL;
569 }
570 ipu_cm_write(ipu, val, IPU_CONF);
571
572 spin_unlock_irqrestore(&ipu->lock, flags);
573}
574EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
575
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200576struct ipu_devtype {
577 const char *name;
578 unsigned long cm_ofs;
579 unsigned long cpmem_ofs;
580 unsigned long srm_ofs;
581 unsigned long tpm_ofs;
Steve Longerbeam2ffd48f2014-08-19 10:52:40 -0700582 unsigned long csi0_ofs;
583 unsigned long csi1_ofs;
Steve Longerbeam1aa8ea02014-08-11 13:04:50 +0200584 unsigned long ic_ofs;
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200585 unsigned long disp0_ofs;
586 unsigned long disp1_ofs;
587 unsigned long dc_tmpl_ofs;
588 unsigned long vdi_ofs;
589 enum ipuv3_type type;
590};
591
592static struct ipu_devtype ipu_type_imx51 = {
593 .name = "IPUv3EX",
594 .cm_ofs = 0x1e000000,
595 .cpmem_ofs = 0x1f000000,
596 .srm_ofs = 0x1f040000,
597 .tpm_ofs = 0x1f060000,
Steve Longerbeam2ffd48f2014-08-19 10:52:40 -0700598 .csi0_ofs = 0x1f030000,
599 .csi1_ofs = 0x1f038000,
Steve Longerbeam1aa8ea02014-08-11 13:04:50 +0200600 .ic_ofs = 0x1f020000,
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200601 .disp0_ofs = 0x1e040000,
602 .disp1_ofs = 0x1e048000,
603 .dc_tmpl_ofs = 0x1f080000,
604 .vdi_ofs = 0x1e068000,
605 .type = IPUV3EX,
606};
607
608static struct ipu_devtype ipu_type_imx53 = {
609 .name = "IPUv3M",
610 .cm_ofs = 0x06000000,
611 .cpmem_ofs = 0x07000000,
612 .srm_ofs = 0x07040000,
613 .tpm_ofs = 0x07060000,
Steve Longerbeam2ffd48f2014-08-19 10:52:40 -0700614 .csi0_ofs = 0x07030000,
615 .csi1_ofs = 0x07038000,
Steve Longerbeam1aa8ea02014-08-11 13:04:50 +0200616 .ic_ofs = 0x07020000,
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200617 .disp0_ofs = 0x06040000,
618 .disp1_ofs = 0x06048000,
619 .dc_tmpl_ofs = 0x07080000,
620 .vdi_ofs = 0x06068000,
621 .type = IPUV3M,
622};
623
624static struct ipu_devtype ipu_type_imx6q = {
625 .name = "IPUv3H",
626 .cm_ofs = 0x00200000,
627 .cpmem_ofs = 0x00300000,
628 .srm_ofs = 0x00340000,
629 .tpm_ofs = 0x00360000,
Steve Longerbeam2ffd48f2014-08-19 10:52:40 -0700630 .csi0_ofs = 0x00230000,
631 .csi1_ofs = 0x00238000,
Steve Longerbeam1aa8ea02014-08-11 13:04:50 +0200632 .ic_ofs = 0x00220000,
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200633 .disp0_ofs = 0x00240000,
634 .disp1_ofs = 0x00248000,
635 .dc_tmpl_ofs = 0x00380000,
636 .vdi_ofs = 0x00268000,
637 .type = IPUV3H,
638};
639
640static const struct of_device_id imx_ipu_dt_ids[] = {
641 { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, },
642 { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, },
643 { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, },
644 { /* sentinel */ }
645};
646MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids);
647
648static int ipu_submodules_init(struct ipu_soc *ipu,
649 struct platform_device *pdev, unsigned long ipu_base,
650 struct clk *ipu_clk)
651{
652 char *unit;
653 int ret;
654 struct device *dev = &pdev->dev;
655 const struct ipu_devtype *devtype = ipu->devtype;
656
Steve Longerbeam7d2691d2014-06-25 18:05:47 -0700657 ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs);
658 if (ret) {
659 unit = "cpmem";
660 goto err_cpmem;
661 }
662
Steve Longerbeam2ffd48f2014-08-19 10:52:40 -0700663 ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs,
664 IPU_CONF_CSI0_EN, ipu_clk);
665 if (ret) {
666 unit = "csi0";
667 goto err_csi_0;
668 }
669
670 ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs,
671 IPU_CONF_CSI1_EN, ipu_clk);
672 if (ret) {
673 unit = "csi1";
674 goto err_csi_1;
675 }
676
Steve Longerbeam1aa8ea02014-08-11 13:04:50 +0200677 ret = ipu_ic_init(ipu, dev,
678 ipu_base + devtype->ic_ofs,
679 ipu_base + devtype->tpm_ofs);
680 if (ret) {
681 unit = "ic";
682 goto err_ic;
683 }
684
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200685 ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
Steve Longerbeam1aa8ea02014-08-11 13:04:50 +0200686 IPU_CONF_DI0_EN, ipu_clk);
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200687 if (ret) {
688 unit = "di0";
689 goto err_di_0;
690 }
691
692 ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs,
693 IPU_CONF_DI1_EN, ipu_clk);
694 if (ret) {
695 unit = "di1";
696 goto err_di_1;
697 }
698
699 ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs +
700 IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs);
701 if (ret) {
702 unit = "dc_template";
703 goto err_dc;
704 }
705
706 ret = ipu_dmfc_init(ipu, dev, ipu_base +
707 devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk);
708 if (ret) {
709 unit = "dmfc";
710 goto err_dmfc;
711 }
712
713 ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs);
714 if (ret) {
715 unit = "dp";
716 goto err_dp;
717 }
718
Philipp Zabel35de9252012-05-09 16:59:01 +0200719 ret = ipu_smfc_init(ipu, dev, ipu_base +
720 devtype->cm_ofs + IPU_CM_SMFC_REG_OFS);
721 if (ret) {
722 unit = "smfc";
723 goto err_smfc;
724 }
725
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200726 return 0;
727
Philipp Zabel35de9252012-05-09 16:59:01 +0200728err_smfc:
729 ipu_dp_exit(ipu);
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200730err_dp:
731 ipu_dmfc_exit(ipu);
732err_dmfc:
733 ipu_dc_exit(ipu);
734err_dc:
735 ipu_di_exit(ipu, 1);
736err_di_1:
737 ipu_di_exit(ipu, 0);
738err_di_0:
Steve Longerbeam1aa8ea02014-08-11 13:04:50 +0200739 ipu_ic_exit(ipu);
740err_ic:
Steve Longerbeam2ffd48f2014-08-19 10:52:40 -0700741 ipu_csi_exit(ipu, 1);
742err_csi_1:
743 ipu_csi_exit(ipu, 0);
744err_csi_0:
Steve Longerbeam7d2691d2014-06-25 18:05:47 -0700745 ipu_cpmem_exit(ipu);
746err_cpmem:
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200747 dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret);
748 return ret;
749}
750
751static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs)
752{
753 unsigned long status;
Philipp Zabelb7287662013-06-21 10:27:39 +0200754 int i, bit, irq;
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200755
756 for (i = 0; i < num_regs; i++) {
757
758 status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i]));
759 status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i]));
760
Philipp Zabelb7287662013-06-21 10:27:39 +0200761 for_each_set_bit(bit, &status, 32) {
Antoine Schweitzer-Chaput838201a2014-04-18 23:20:06 +0200762 irq = irq_linear_revmap(ipu->domain,
763 regs[i] * 32 + bit);
Philipp Zabelb7287662013-06-21 10:27:39 +0200764 if (irq)
765 generic_handle_irq(irq);
766 }
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200767 }
768}
769
770static void ipu_irq_handler(unsigned int irq, struct irq_desc *desc)
771{
772 struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
773 const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14};
774 struct irq_chip *chip = irq_get_chip(irq);
775
776 chained_irq_enter(chip, desc);
777
778 ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
779
780 chained_irq_exit(chip, desc);
781}
782
783static void ipu_err_irq_handler(unsigned int irq, struct irq_desc *desc)
784{
785 struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
786 const int int_reg[] = { 4, 5, 8, 9};
787 struct irq_chip *chip = irq_get_chip(irq);
788
789 chained_irq_enter(chip, desc);
790
791 ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
792
793 chained_irq_exit(chip, desc);
794}
795
Philipp Zabel861a50c2014-04-14 23:53:16 +0200796int ipu_map_irq(struct ipu_soc *ipu, int irq)
797{
798 int virq;
799
800 virq = irq_linear_revmap(ipu->domain, irq);
801 if (!virq)
802 virq = irq_create_mapping(ipu->domain, irq);
803
804 return virq;
805}
806EXPORT_SYMBOL_GPL(ipu_map_irq);
807
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200808int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
809 enum ipu_channel_irq irq_type)
810{
Philipp Zabel861a50c2014-04-14 23:53:16 +0200811 return ipu_map_irq(ipu, irq_type + channel->num);
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200812}
813EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq);
814
815static void ipu_submodules_exit(struct ipu_soc *ipu)
816{
Philipp Zabel35de9252012-05-09 16:59:01 +0200817 ipu_smfc_exit(ipu);
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200818 ipu_dp_exit(ipu);
819 ipu_dmfc_exit(ipu);
820 ipu_dc_exit(ipu);
821 ipu_di_exit(ipu, 1);
822 ipu_di_exit(ipu, 0);
Steve Longerbeam1aa8ea02014-08-11 13:04:50 +0200823 ipu_ic_exit(ipu);
Steve Longerbeam2ffd48f2014-08-19 10:52:40 -0700824 ipu_csi_exit(ipu, 1);
825 ipu_csi_exit(ipu, 0);
Steve Longerbeam7d2691d2014-06-25 18:05:47 -0700826 ipu_cpmem_exit(ipu);
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200827}
828
829static int platform_remove_devices_fn(struct device *dev, void *unused)
830{
831 struct platform_device *pdev = to_platform_device(dev);
832
833 platform_device_unregister(pdev);
834
835 return 0;
836}
837
838static void platform_device_unregister_children(struct platform_device *pdev)
839{
840 device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn);
841}
842
843struct ipu_platform_reg {
844 struct ipu_client_platformdata pdata;
845 const char *name;
Philipp Zabeld6ca8ca2012-05-23 17:08:19 +0200846 int reg_offset;
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200847};
848
849static const struct ipu_platform_reg client_reg[] = {
850 {
851 .pdata = {
852 .di = 0,
853 .dc = 5,
854 .dp = IPU_DP_FLOW_SYNC_BG,
855 .dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC,
Philipp Zabelb8d181e2013-10-10 16:18:45 +0200856 .dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC,
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200857 },
858 .name = "imx-ipuv3-crtc",
859 }, {
860 .pdata = {
861 .di = 1,
862 .dc = 1,
863 .dp = -EINVAL,
864 .dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC,
865 .dma[1] = -EINVAL,
866 },
867 .name = "imx-ipuv3-crtc",
Philipp Zabeld6ca8ca2012-05-23 17:08:19 +0200868 }, {
869 .pdata = {
870 .csi = 0,
871 .dma[0] = IPUV3_CHANNEL_CSI0,
872 .dma[1] = -EINVAL,
873 },
874 .reg_offset = IPU_CM_CSI0_REG_OFS,
875 .name = "imx-ipuv3-camera",
876 }, {
877 .pdata = {
878 .csi = 1,
879 .dma[0] = IPUV3_CHANNEL_CSI1,
880 .dma[1] = -EINVAL,
881 },
882 .reg_offset = IPU_CM_CSI1_REG_OFS,
883 .name = "imx-ipuv3-camera",
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200884 },
885};
886
Russell King4ae078d2013-12-16 11:34:25 +0000887static DEFINE_MUTEX(ipu_client_id_mutex);
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200888static int ipu_client_id;
889
Philipp Zabeld6ca8ca2012-05-23 17:08:19 +0200890static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base)
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200891{
Russell King4ae078d2013-12-16 11:34:25 +0000892 struct device *dev = ipu->dev;
893 unsigned i;
894 int id, ret;
895
896 mutex_lock(&ipu_client_id_mutex);
897 id = ipu_client_id;
898 ipu_client_id += ARRAY_SIZE(client_reg);
899 mutex_unlock(&ipu_client_id_mutex);
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200900
901 for (i = 0; i < ARRAY_SIZE(client_reg); i++) {
902 const struct ipu_platform_reg *reg = &client_reg[i];
Russell King4ae078d2013-12-16 11:34:25 +0000903 struct platform_device *pdev;
Philipp Zabeld6ca8ca2012-05-23 17:08:19 +0200904 struct resource res;
Russell King4ae078d2013-12-16 11:34:25 +0000905
Philipp Zabeld6ca8ca2012-05-23 17:08:19 +0200906 if (reg->reg_offset) {
907 memset(&res, 0, sizeof(res));
908 res.flags = IORESOURCE_MEM;
909 res.start = ipu_base + ipu->devtype->cm_ofs + reg->reg_offset;
910 res.end = res.start + PAGE_SIZE - 1;
911 pdev = platform_device_register_resndata(dev, reg->name,
912 id++, &res, 1, &reg->pdata, sizeof(reg->pdata));
913 } else {
914 pdev = platform_device_register_data(dev, reg->name,
915 id++, &reg->pdata, sizeof(reg->pdata));
916 }
Russell King4ae078d2013-12-16 11:34:25 +0000917
918 if (IS_ERR(pdev))
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200919 goto err_register;
920 }
921
922 return 0;
923
924err_register:
Russell King4ae078d2013-12-16 11:34:25 +0000925 platform_device_unregister_children(to_platform_device(dev));
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200926
927 return ret;
928}
929
Philipp Zabelb7287662013-06-21 10:27:39 +0200930
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200931static int ipu_irq_init(struct ipu_soc *ipu)
932{
Philipp Zabel379cdec2013-06-21 14:52:17 +0200933 struct irq_chip_generic *gc;
934 struct irq_chip_type *ct;
Philipp Zabel37f85b262013-06-21 14:52:18 +0200935 unsigned long unused[IPU_NUM_IRQS / 32] = {
936 0x400100d0, 0xffe000fd,
937 0x400100d0, 0xffe000fd,
938 0x400100d0, 0xffe000fd,
939 0x4077ffff, 0xffe7e1fd,
940 0x23fffffe, 0x8880fff0,
941 0xf98fe7d0, 0xfff81fff,
942 0x400100d0, 0xffe000fd,
943 0x00000000,
944 };
Philipp Zabel379cdec2013-06-21 14:52:17 +0200945 int ret, i;
946
Philipp Zabelb7287662013-06-21 10:27:39 +0200947 ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS,
Philipp Zabel379cdec2013-06-21 14:52:17 +0200948 &irq_generic_chip_ops, ipu);
Philipp Zabelb7287662013-06-21 10:27:39 +0200949 if (!ipu->domain) {
950 dev_err(ipu->dev, "failed to add irq domain\n");
951 return -ENODEV;
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200952 }
953
Philipp Zabel379cdec2013-06-21 14:52:17 +0200954 ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU",
Antoine Schweitzer-Chaput838201a2014-04-18 23:20:06 +0200955 handle_level_irq, 0,
956 IRQF_VALID, 0);
Philipp Zabel379cdec2013-06-21 14:52:17 +0200957 if (ret < 0) {
958 dev_err(ipu->dev, "failed to alloc generic irq chips\n");
959 irq_domain_remove(ipu->domain);
960 return ret;
961 }
962
963 for (i = 0; i < IPU_NUM_IRQS; i += 32) {
964 gc = irq_get_domain_generic_chip(ipu->domain, i);
965 gc->reg_base = ipu->cm_reg;
Philipp Zabel37f85b262013-06-21 14:52:18 +0200966 gc->unused = unused[i / 32];
Philipp Zabel379cdec2013-06-21 14:52:17 +0200967 ct = gc->chip_types;
968 ct->chip.irq_ack = irq_gc_ack_set_bit;
969 ct->chip.irq_mask = irq_gc_mask_clr_bit;
970 ct->chip.irq_unmask = irq_gc_mask_set_bit;
971 ct->regs.ack = IPU_INT_STAT(i / 32);
972 ct->regs.mask = IPU_INT_CTRL(i / 32);
973 }
974
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200975 irq_set_chained_handler(ipu->irq_sync, ipu_irq_handler);
976 irq_set_handler_data(ipu->irq_sync, ipu);
977 irq_set_chained_handler(ipu->irq_err, ipu_err_irq_handler);
978 irq_set_handler_data(ipu->irq_err, ipu);
979
980 return 0;
981}
982
983static void ipu_irq_exit(struct ipu_soc *ipu)
984{
Philipp Zabelb7287662013-06-21 10:27:39 +0200985 int i, irq;
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200986
987 irq_set_chained_handler(ipu->irq_err, NULL);
988 irq_set_handler_data(ipu->irq_err, NULL);
989 irq_set_chained_handler(ipu->irq_sync, NULL);
990 irq_set_handler_data(ipu->irq_sync, NULL);
991
Philipp Zabel379cdec2013-06-21 14:52:17 +0200992 /* TODO: remove irq_domain_generic_chips */
993
Philipp Zabelb7287662013-06-21 10:27:39 +0200994 for (i = 0; i < IPU_NUM_IRQS; i++) {
995 irq = irq_linear_revmap(ipu->domain, i);
996 if (irq)
997 irq_dispose_mapping(irq);
Sascha Haueraecfbdb2012-09-21 10:07:49 +0200998 }
999
Philipp Zabelb7287662013-06-21 10:27:39 +02001000 irq_domain_remove(ipu->domain);
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001001}
1002
Bill Pembertonc4aabf82012-11-19 13:22:11 -05001003static int ipu_probe(struct platform_device *pdev)
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001004{
1005 const struct of_device_id *of_id =
1006 of_match_device(imx_ipu_dt_ids, &pdev->dev);
1007 struct ipu_soc *ipu;
1008 struct resource *res;
1009 unsigned long ipu_base;
1010 int i, ret, irq_sync, irq_err;
1011 const struct ipu_devtype *devtype;
1012
1013 devtype = of_id->data;
1014
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001015 irq_sync = platform_get_irq(pdev, 0);
1016 irq_err = platform_get_irq(pdev, 1);
1017 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1018
Fabio Estevamfd563db2012-10-24 21:36:46 -02001019 dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n",
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001020 irq_sync, irq_err);
1021
1022 if (!res || irq_sync < 0 || irq_err < 0)
1023 return -ENODEV;
1024
1025 ipu_base = res->start;
1026
1027 ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL);
1028 if (!ipu)
1029 return -ENODEV;
1030
1031 for (i = 0; i < 64; i++)
1032 ipu->channel[i].ipu = ipu;
1033 ipu->devtype = devtype;
1034 ipu->ipu_type = devtype->type;
1035
1036 spin_lock_init(&ipu->lock);
1037 mutex_init(&ipu->channel_lock);
1038
Fabio Estevamfd563db2012-10-24 21:36:46 -02001039 dev_dbg(&pdev->dev, "cm_reg: 0x%08lx\n",
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001040 ipu_base + devtype->cm_ofs);
Fabio Estevamfd563db2012-10-24 21:36:46 -02001041 dev_dbg(&pdev->dev, "idmac: 0x%08lx\n",
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001042 ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS);
Fabio Estevamfd563db2012-10-24 21:36:46 -02001043 dev_dbg(&pdev->dev, "cpmem: 0x%08lx\n",
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001044 ipu_base + devtype->cpmem_ofs);
Steve Longerbeam2ffd48f2014-08-19 10:52:40 -07001045 dev_dbg(&pdev->dev, "csi0: 0x%08lx\n",
1046 ipu_base + devtype->csi0_ofs);
1047 dev_dbg(&pdev->dev, "csi1: 0x%08lx\n",
1048 ipu_base + devtype->csi1_ofs);
Steve Longerbeam1aa8ea02014-08-11 13:04:50 +02001049 dev_dbg(&pdev->dev, "ic: 0x%08lx\n",
1050 ipu_base + devtype->ic_ofs);
Fabio Estevamfd563db2012-10-24 21:36:46 -02001051 dev_dbg(&pdev->dev, "disp0: 0x%08lx\n",
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001052 ipu_base + devtype->disp0_ofs);
Fabio Estevamfd563db2012-10-24 21:36:46 -02001053 dev_dbg(&pdev->dev, "disp1: 0x%08lx\n",
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001054 ipu_base + devtype->disp1_ofs);
Fabio Estevamfd563db2012-10-24 21:36:46 -02001055 dev_dbg(&pdev->dev, "srm: 0x%08lx\n",
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001056 ipu_base + devtype->srm_ofs);
Fabio Estevamfd563db2012-10-24 21:36:46 -02001057 dev_dbg(&pdev->dev, "tpm: 0x%08lx\n",
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001058 ipu_base + devtype->tpm_ofs);
Fabio Estevamfd563db2012-10-24 21:36:46 -02001059 dev_dbg(&pdev->dev, "dc: 0x%08lx\n",
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001060 ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS);
Fabio Estevamfd563db2012-10-24 21:36:46 -02001061 dev_dbg(&pdev->dev, "ic: 0x%08lx\n",
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001062 ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS);
Fabio Estevamfd563db2012-10-24 21:36:46 -02001063 dev_dbg(&pdev->dev, "dmfc: 0x%08lx\n",
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001064 ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS);
Fabio Estevamfd563db2012-10-24 21:36:46 -02001065 dev_dbg(&pdev->dev, "vdi: 0x%08lx\n",
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001066 ipu_base + devtype->vdi_ofs);
1067
1068 ipu->cm_reg = devm_ioremap(&pdev->dev,
1069 ipu_base + devtype->cm_ofs, PAGE_SIZE);
1070 ipu->idmac_reg = devm_ioremap(&pdev->dev,
1071 ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS,
1072 PAGE_SIZE);
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001073
Steve Longerbeam7d2691d2014-06-25 18:05:47 -07001074 if (!ipu->cm_reg || !ipu->idmac_reg)
Fabio Estevambe798b22013-07-20 18:22:09 -03001075 return -ENOMEM;
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001076
1077 ipu->clk = devm_clk_get(&pdev->dev, "bus");
1078 if (IS_ERR(ipu->clk)) {
1079 ret = PTR_ERR(ipu->clk);
1080 dev_err(&pdev->dev, "clk_get failed with %d", ret);
Fabio Estevambe798b22013-07-20 18:22:09 -03001081 return ret;
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001082 }
1083
1084 platform_set_drvdata(pdev, ipu);
1085
Fabio Estevam62645a22013-07-20 18:22:10 -03001086 ret = clk_prepare_enable(ipu->clk);
1087 if (ret) {
1088 dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret);
1089 return ret;
1090 }
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001091
1092 ipu->dev = &pdev->dev;
1093 ipu->irq_sync = irq_sync;
1094 ipu->irq_err = irq_err;
1095
1096 ret = ipu_irq_init(ipu);
1097 if (ret)
1098 goto out_failed_irq;
1099
Philipp Zabel6c641552013-03-28 17:35:21 +01001100 ret = device_reset(&pdev->dev);
1101 if (ret) {
1102 dev_err(&pdev->dev, "failed to reset: %d\n", ret);
1103 goto out_failed_reset;
1104 }
1105 ret = ipu_memory_reset(ipu);
Lothar Waßmann4d27b2c2012-12-25 15:58:37 +01001106 if (ret)
1107 goto out_failed_reset;
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001108
1109 /* Set MCU_T to divide MCU access window into 2 */
1110 ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18),
1111 IPU_DISP_GEN);
1112
1113 ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk);
1114 if (ret)
1115 goto failed_submodules_init;
1116
Philipp Zabeld6ca8ca2012-05-23 17:08:19 +02001117 ret = ipu_add_client_devices(ipu, ipu_base);
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001118 if (ret) {
1119 dev_err(&pdev->dev, "adding client devices failed with %d\n",
1120 ret);
1121 goto failed_add_clients;
1122 }
1123
Fabio Estevam9c2c4382012-10-24 21:36:47 -02001124 dev_info(&pdev->dev, "%s probed\n", devtype->name);
1125
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001126 return 0;
1127
1128failed_add_clients:
1129 ipu_submodules_exit(ipu);
1130failed_submodules_init:
Lothar Waßmann4d27b2c2012-12-25 15:58:37 +01001131out_failed_reset:
Philipp Zabel6c641552013-03-28 17:35:21 +01001132 ipu_irq_exit(ipu);
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001133out_failed_irq:
1134 clk_disable_unprepare(ipu->clk);
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001135 return ret;
1136}
1137
Bill Pemberton8aa1be42012-11-19 13:26:38 -05001138static int ipu_remove(struct platform_device *pdev)
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001139{
1140 struct ipu_soc *ipu = platform_get_drvdata(pdev);
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001141
1142 platform_device_unregister_children(pdev);
1143 ipu_submodules_exit(ipu);
1144 ipu_irq_exit(ipu);
1145
1146 clk_disable_unprepare(ipu->clk);
1147
1148 return 0;
1149}
1150
1151static struct platform_driver imx_ipu_driver = {
1152 .driver = {
1153 .name = "imx-ipuv3",
1154 .of_match_table = imx_ipu_dt_ids,
1155 },
1156 .probe = ipu_probe,
Bill Pemberton99c28f12012-11-19 13:20:51 -05001157 .remove = ipu_remove,
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001158};
1159
1160module_platform_driver(imx_ipu_driver);
1161
Fabio Estevam10f22682013-07-20 18:22:11 -03001162MODULE_ALIAS("platform:imx-ipuv3");
Sascha Haueraecfbdb2012-09-21 10:07:49 +02001163MODULE_DESCRIPTION("i.MX IPU v3 driver");
1164MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
1165MODULE_LICENSE("GPL");