Linux Audio

Check our new training course

Embedded Linux training

Mar 10-20, 2025, special US time zones
Register
Loading...
Note: File does not exist in v6.13.7.
  1/*
  2 * Copyright 2016 Linaro Ltd.
  3 * Copyright 2016 ZTE Corporation.
  4 *
  5 * This program is free software; you can redistribute it and/or modify
  6 * it under the terms of the GNU General Public License version 2 as
  7 * published by the Free Software Foundation.
  8 *
  9 */
 10
 11#include <linux/clk.h>
 12#include <linux/component.h>
 13#include <linux/of_address.h>
 14#include <video/videomode.h>
 15
 16#include <drm/drm_atomic_helper.h>
 17#include <drm/drm_crtc.h>
 18#include <drm/drm_crtc_helper.h>
 19#include <drm/drm_fb_cma_helper.h>
 20#include <drm/drm_fb_helper.h>
 21#include <drm/drm_gem_cma_helper.h>
 22#include <drm/drm_of.h>
 23#include <drm/drm_plane_helper.h>
 24#include <drm/drmP.h>
 25
 26#include "zx_drm_drv.h"
 27#include "zx_plane.h"
 28#include "zx_vou.h"
 29#include "zx_vou_regs.h"
 30
 31#define GL_NUM	2
 32#define VL_NUM	3
 33
 34enum vou_chn_type {
 35	VOU_CHN_MAIN,
 36	VOU_CHN_AUX,
 37};
 38
 39struct zx_crtc_regs {
 40	u32 fir_active;
 41	u32 fir_htiming;
 42	u32 fir_vtiming;
 43	u32 timing_shift;
 44	u32 timing_pi_shift;
 45};
 46
 47static const struct zx_crtc_regs main_crtc_regs = {
 48	.fir_active = FIR_MAIN_ACTIVE,
 49	.fir_htiming = FIR_MAIN_H_TIMING,
 50	.fir_vtiming = FIR_MAIN_V_TIMING,
 51	.timing_shift = TIMING_MAIN_SHIFT,
 52	.timing_pi_shift = TIMING_MAIN_PI_SHIFT,
 53};
 54
 55static const struct zx_crtc_regs aux_crtc_regs = {
 56	.fir_active = FIR_AUX_ACTIVE,
 57	.fir_htiming = FIR_AUX_H_TIMING,
 58	.fir_vtiming = FIR_AUX_V_TIMING,
 59	.timing_shift = TIMING_AUX_SHIFT,
 60	.timing_pi_shift = TIMING_AUX_PI_SHIFT,
 61};
 62
 63struct zx_crtc_bits {
 64	u32 polarity_mask;
 65	u32 polarity_shift;
 66	u32 int_frame_mask;
 67	u32 tc_enable;
 68	u32 gl_enable;
 69};
 70
 71static const struct zx_crtc_bits main_crtc_bits = {
 72	.polarity_mask = MAIN_POL_MASK,
 73	.polarity_shift = MAIN_POL_SHIFT,
 74	.int_frame_mask = TIMING_INT_MAIN_FRAME,
 75	.tc_enable = MAIN_TC_EN,
 76	.gl_enable = OSD_CTRL0_GL0_EN,
 77};
 78
 79static const struct zx_crtc_bits aux_crtc_bits = {
 80	.polarity_mask = AUX_POL_MASK,
 81	.polarity_shift = AUX_POL_SHIFT,
 82	.int_frame_mask = TIMING_INT_AUX_FRAME,
 83	.tc_enable = AUX_TC_EN,
 84	.gl_enable = OSD_CTRL0_GL1_EN,
 85};
 86
 87struct zx_crtc {
 88	struct drm_crtc crtc;
 89	struct drm_plane *primary;
 90	struct zx_vou_hw *vou;
 91	void __iomem *chnreg;
 92	const struct zx_crtc_regs *regs;
 93	const struct zx_crtc_bits *bits;
 94	enum vou_chn_type chn_type;
 95	struct clk *pixclk;
 96};
 97
 98#define to_zx_crtc(x) container_of(x, struct zx_crtc, crtc)
 99
100struct zx_vou_hw {
101	struct device *dev;
102	void __iomem *osd;
103	void __iomem *timing;
104	void __iomem *vouctl;
105	void __iomem *otfppu;
106	void __iomem *dtrc;
107	struct clk *axi_clk;
108	struct clk *ppu_clk;
109	struct clk *main_clk;
110	struct clk *aux_clk;
111	struct zx_crtc *main_crtc;
112	struct zx_crtc *aux_crtc;
113};
114
115static inline struct zx_vou_hw *crtc_to_vou(struct drm_crtc *crtc)
116{
117	struct zx_crtc *zcrtc = to_zx_crtc(crtc);
118
119	return zcrtc->vou;
120}
121
122void vou_inf_enable(const struct vou_inf *inf, struct drm_crtc *crtc)
123{
124	struct zx_crtc *zcrtc = to_zx_crtc(crtc);
125	struct zx_vou_hw *vou = zcrtc->vou;
126	bool is_main = zcrtc->chn_type == VOU_CHN_MAIN;
127	u32 data_sel_shift = inf->id << 1;
128
129	/* Select data format */
130	zx_writel_mask(vou->vouctl + VOU_INF_DATA_SEL, 0x3 << data_sel_shift,
131		       inf->data_sel << data_sel_shift);
132
133	/* Select channel */
134	zx_writel_mask(vou->vouctl + VOU_INF_CH_SEL, 0x1 << inf->id,
135		       zcrtc->chn_type << inf->id);
136
137	/* Select interface clocks */
138	zx_writel_mask(vou->vouctl + VOU_CLK_SEL, inf->clocks_sel_bits,
139		       is_main ? 0 : inf->clocks_sel_bits);
140
141	/* Enable interface clocks */
142	zx_writel_mask(vou->vouctl + VOU_CLK_EN, inf->clocks_en_bits,
143		       inf->clocks_en_bits);
144
145	/* Enable the device */
146	zx_writel_mask(vou->vouctl + VOU_INF_EN, 1 << inf->id, 1 << inf->id);
147}
148
149void vou_inf_disable(const struct vou_inf *inf, struct drm_crtc *crtc)
150{
151	struct zx_vou_hw *vou = crtc_to_vou(crtc);
152
153	/* Disable the device */
154	zx_writel_mask(vou->vouctl + VOU_INF_EN, 1 << inf->id, 0);
155
156	/* Disable interface clocks */
157	zx_writel_mask(vou->vouctl + VOU_CLK_EN, inf->clocks_en_bits, 0);
158}
159
160static inline void vou_chn_set_update(struct zx_crtc *zcrtc)
161{
162	zx_writel(zcrtc->chnreg + CHN_UPDATE, 1);
163}
164
165static void zx_crtc_enable(struct drm_crtc *crtc)
166{
167	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
168	struct zx_crtc *zcrtc = to_zx_crtc(crtc);
169	struct zx_vou_hw *vou = zcrtc->vou;
170	const struct zx_crtc_regs *regs = zcrtc->regs;
171	const struct zx_crtc_bits *bits = zcrtc->bits;
172	struct videomode vm;
173	u32 pol = 0;
174	u32 val;
175	int ret;
176
177	drm_display_mode_to_videomode(mode, &vm);
178
179	/* Set up timing parameters */
180	val = V_ACTIVE(vm.vactive - 1);
181	val |= H_ACTIVE(vm.hactive - 1);
182	zx_writel(vou->timing + regs->fir_active, val);
183
184	val = SYNC_WIDE(vm.hsync_len - 1);
185	val |= BACK_PORCH(vm.hback_porch - 1);
186	val |= FRONT_PORCH(vm.hfront_porch - 1);
187	zx_writel(vou->timing + regs->fir_htiming, val);
188
189	val = SYNC_WIDE(vm.vsync_len - 1);
190	val |= BACK_PORCH(vm.vback_porch - 1);
191	val |= FRONT_PORCH(vm.vfront_porch - 1);
192	zx_writel(vou->timing + regs->fir_vtiming, val);
193
194	/* Set up polarities */
195	if (vm.flags & DISPLAY_FLAGS_VSYNC_LOW)
196		pol |= 1 << POL_VSYNC_SHIFT;
197	if (vm.flags & DISPLAY_FLAGS_HSYNC_LOW)
198		pol |= 1 << POL_HSYNC_SHIFT;
199
200	zx_writel_mask(vou->timing + TIMING_CTRL, bits->polarity_mask,
201		       pol << bits->polarity_shift);
202
203	/* Setup SHIFT register by following what ZTE BSP does */
204	zx_writel(vou->timing + regs->timing_shift, H_SHIFT_VAL);
205	zx_writel(vou->timing + regs->timing_pi_shift, H_PI_SHIFT_VAL);
206
207	/* Enable TIMING_CTRL */
208	zx_writel_mask(vou->timing + TIMING_TC_ENABLE, bits->tc_enable,
209		       bits->tc_enable);
210
211	/* Configure channel screen size */
212	zx_writel_mask(zcrtc->chnreg + CHN_CTRL1, CHN_SCREEN_W_MASK,
213		       vm.hactive << CHN_SCREEN_W_SHIFT);
214	zx_writel_mask(zcrtc->chnreg + CHN_CTRL1, CHN_SCREEN_H_MASK,
215		       vm.vactive << CHN_SCREEN_H_SHIFT);
216
217	/* Update channel */
218	vou_chn_set_update(zcrtc);
219
220	/* Enable channel */
221	zx_writel_mask(zcrtc->chnreg + CHN_CTRL0, CHN_ENABLE, CHN_ENABLE);
222
223	/* Enable Graphic Layer */
224	zx_writel_mask(vou->osd + OSD_CTRL0, bits->gl_enable,
225		       bits->gl_enable);
226
227	drm_crtc_vblank_on(crtc);
228
229	ret = clk_set_rate(zcrtc->pixclk, mode->clock * 1000);
230	if (ret) {
231		DRM_DEV_ERROR(vou->dev, "failed to set pixclk rate: %d\n", ret);
232		return;
233	}
234
235	ret = clk_prepare_enable(zcrtc->pixclk);
236	if (ret)
237		DRM_DEV_ERROR(vou->dev, "failed to enable pixclk: %d\n", ret);
238}
239
240static void zx_crtc_disable(struct drm_crtc *crtc)
241{
242	struct zx_crtc *zcrtc = to_zx_crtc(crtc);
243	const struct zx_crtc_bits *bits = zcrtc->bits;
244	struct zx_vou_hw *vou = zcrtc->vou;
245
246	clk_disable_unprepare(zcrtc->pixclk);
247
248	drm_crtc_vblank_off(crtc);
249
250	/* Disable Graphic Layer */
251	zx_writel_mask(vou->osd + OSD_CTRL0, bits->gl_enable, 0);
252
253	/* Disable channel */
254	zx_writel_mask(zcrtc->chnreg + CHN_CTRL0, CHN_ENABLE, 0);
255
256	/* Disable TIMING_CTRL */
257	zx_writel_mask(vou->timing + TIMING_TC_ENABLE, bits->tc_enable, 0);
258}
259
260static void zx_crtc_atomic_flush(struct drm_crtc *crtc,
261				  struct drm_crtc_state *old_state)
262{
263	struct drm_pending_vblank_event *event = crtc->state->event;
264
265	if (!event)
266		return;
267
268	crtc->state->event = NULL;
269
270	spin_lock_irq(&crtc->dev->event_lock);
271	if (drm_crtc_vblank_get(crtc) == 0)
272		drm_crtc_arm_vblank_event(crtc, event);
273	else
274		drm_crtc_send_vblank_event(crtc, event);
275	spin_unlock_irq(&crtc->dev->event_lock);
276}
277
278static const struct drm_crtc_helper_funcs zx_crtc_helper_funcs = {
279	.enable = zx_crtc_enable,
280	.disable = zx_crtc_disable,
281	.atomic_flush = zx_crtc_atomic_flush,
282};
283
284static const struct drm_crtc_funcs zx_crtc_funcs = {
285	.destroy = drm_crtc_cleanup,
286	.set_config = drm_atomic_helper_set_config,
287	.page_flip = drm_atomic_helper_page_flip,
288	.reset = drm_atomic_helper_crtc_reset,
289	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
290	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
291};
292
293static int zx_crtc_init(struct drm_device *drm, struct zx_vou_hw *vou,
294			enum vou_chn_type chn_type)
295{
296	struct device *dev = vou->dev;
297	struct zx_layer_data data;
298	struct zx_crtc *zcrtc;
299	int ret;
300
301	zcrtc = devm_kzalloc(dev, sizeof(*zcrtc), GFP_KERNEL);
302	if (!zcrtc)
303		return -ENOMEM;
304
305	zcrtc->vou = vou;
306	zcrtc->chn_type = chn_type;
307
308	if (chn_type == VOU_CHN_MAIN) {
309		data.layer = vou->osd + MAIN_GL_OFFSET;
310		data.csc = vou->osd + MAIN_CSC_OFFSET;
311		data.hbsc = vou->osd + MAIN_HBSC_OFFSET;
312		data.rsz = vou->otfppu + MAIN_RSZ_OFFSET;
313		zcrtc->chnreg = vou->osd + OSD_MAIN_CHN;
314		zcrtc->regs = &main_crtc_regs;
315		zcrtc->bits = &main_crtc_bits;
316	} else {
317		data.layer = vou->osd + AUX_GL_OFFSET;
318		data.csc = vou->osd + AUX_CSC_OFFSET;
319		data.hbsc = vou->osd + AUX_HBSC_OFFSET;
320		data.rsz = vou->otfppu + AUX_RSZ_OFFSET;
321		zcrtc->chnreg = vou->osd + OSD_AUX_CHN;
322		zcrtc->regs = &aux_crtc_regs;
323		zcrtc->bits = &aux_crtc_bits;
324	}
325
326	zcrtc->pixclk = devm_clk_get(dev, (chn_type == VOU_CHN_MAIN) ?
327					  "main_wclk" : "aux_wclk");
328	if (IS_ERR(zcrtc->pixclk)) {
329		ret = PTR_ERR(zcrtc->pixclk);
330		DRM_DEV_ERROR(dev, "failed to get pix clk: %d\n", ret);
331		return ret;
332	}
333
334	zcrtc->primary = zx_plane_init(drm, dev, &data, DRM_PLANE_TYPE_PRIMARY);
335	if (IS_ERR(zcrtc->primary)) {
336		ret = PTR_ERR(zcrtc->primary);
337		DRM_DEV_ERROR(dev, "failed to init primary plane: %d\n", ret);
338		return ret;
339	}
340
341	ret = drm_crtc_init_with_planes(drm, &zcrtc->crtc, zcrtc->primary, NULL,
342					&zx_crtc_funcs, NULL);
343	if (ret) {
344		DRM_DEV_ERROR(dev, "failed to init drm crtc: %d\n", ret);
345		return ret;
346	}
347
348	drm_crtc_helper_add(&zcrtc->crtc, &zx_crtc_helper_funcs);
349
350	if (chn_type == VOU_CHN_MAIN)
351		vou->main_crtc = zcrtc;
352	else
353		vou->aux_crtc = zcrtc;
354
355	return 0;
356}
357
358static inline struct drm_crtc *zx_find_crtc(struct drm_device *drm, int pipe)
359{
360	struct drm_crtc *crtc;
361
362	list_for_each_entry(crtc, &drm->mode_config.crtc_list, head)
363		if (crtc->index == pipe)
364			return crtc;
365
366	return NULL;
367}
368
369int zx_vou_enable_vblank(struct drm_device *drm, unsigned int pipe)
370{
371	struct drm_crtc *crtc;
372	struct zx_crtc *zcrtc;
373	struct zx_vou_hw *vou;
374	u32 int_frame_mask;
375
376	crtc = zx_find_crtc(drm, pipe);
377	if (!crtc)
378		return 0;
379
380	vou = crtc_to_vou(crtc);
381	zcrtc = to_zx_crtc(crtc);
382	int_frame_mask = zcrtc->bits->int_frame_mask;
383
384	zx_writel_mask(vou->timing + TIMING_INT_CTRL, int_frame_mask,
385		       int_frame_mask);
386
387	return 0;
388}
389
390void zx_vou_disable_vblank(struct drm_device *drm, unsigned int pipe)
391{
392	struct drm_crtc *crtc;
393	struct zx_crtc *zcrtc;
394	struct zx_vou_hw *vou;
395
396	crtc = zx_find_crtc(drm, pipe);
397	if (!crtc)
398		return;
399
400	vou = crtc_to_vou(crtc);
401	zcrtc = to_zx_crtc(crtc);
402
403	zx_writel_mask(vou->timing + TIMING_INT_CTRL,
404		       zcrtc->bits->int_frame_mask, 0);
405}
406
407static irqreturn_t vou_irq_handler(int irq, void *dev_id)
408{
409	struct zx_vou_hw *vou = dev_id;
410	u32 state;
411
412	/* Handle TIMING_CTRL frame interrupts */
413	state = zx_readl(vou->timing + TIMING_INT_STATE);
414	zx_writel(vou->timing + TIMING_INT_STATE, state);
415
416	if (state & TIMING_INT_MAIN_FRAME)
417		drm_crtc_handle_vblank(&vou->main_crtc->crtc);
418
419	if (state & TIMING_INT_AUX_FRAME)
420		drm_crtc_handle_vblank(&vou->aux_crtc->crtc);
421
422	/* Handle OSD interrupts */
423	state = zx_readl(vou->osd + OSD_INT_STA);
424	zx_writel(vou->osd + OSD_INT_CLRSTA, state);
425
426	if (state & OSD_INT_MAIN_UPT) {
427		vou_chn_set_update(vou->main_crtc);
428		zx_plane_set_update(vou->main_crtc->primary);
429	}
430
431	if (state & OSD_INT_AUX_UPT) {
432		vou_chn_set_update(vou->aux_crtc);
433		zx_plane_set_update(vou->aux_crtc->primary);
434	}
435
436	if (state & OSD_INT_ERROR)
437		DRM_DEV_ERROR(vou->dev, "OSD ERROR: 0x%08x!\n", state);
438
439	return IRQ_HANDLED;
440}
441
442static void vou_dtrc_init(struct zx_vou_hw *vou)
443{
444	/* Clear bit for bypass by ID */
445	zx_writel_mask(vou->dtrc + DTRC_DETILE_CTRL,
446		       TILE2RASTESCAN_BYPASS_MODE, 0);
447
448	/* Select ARIDR mode */
449	zx_writel_mask(vou->dtrc + DTRC_DETILE_CTRL, DETILE_ARIDR_MODE_MASK,
450		       DETILE_ARID_IN_ARIDR);
451
452	/* Bypass decompression for both frames */
453	zx_writel_mask(vou->dtrc + DTRC_F0_CTRL, DTRC_DECOMPRESS_BYPASS,
454		       DTRC_DECOMPRESS_BYPASS);
455	zx_writel_mask(vou->dtrc + DTRC_F1_CTRL, DTRC_DECOMPRESS_BYPASS,
456		       DTRC_DECOMPRESS_BYPASS);
457
458	/* Set up ARID register */
459	zx_writel(vou->dtrc + DTRC_ARID, DTRC_ARID3(0xf) | DTRC_ARID2(0xe) |
460		  DTRC_ARID1(0xf) | DTRC_ARID0(0xe));
461}
462
463static void vou_hw_init(struct zx_vou_hw *vou)
464{
465	/* Set GL0 to main channel and GL1 to aux channel */
466	zx_writel_mask(vou->osd + OSD_CTRL0, OSD_CTRL0_GL0_SEL, 0);
467	zx_writel_mask(vou->osd + OSD_CTRL0, OSD_CTRL0_GL1_SEL,
468		       OSD_CTRL0_GL1_SEL);
469
470	/* Release reset for all VOU modules */
471	zx_writel(vou->vouctl + VOU_SOFT_RST, ~0);
472
473	/* Select main clock for GL0 and aux clock for GL1 module */
474	zx_writel_mask(vou->vouctl + VOU_CLK_SEL, VOU_CLK_GL0_SEL, 0);
475	zx_writel_mask(vou->vouctl + VOU_CLK_SEL, VOU_CLK_GL1_SEL,
476		       VOU_CLK_GL1_SEL);
477
478	/* Enable clock auto-gating for all VOU modules */
479	zx_writel(vou->vouctl + VOU_CLK_REQEN, ~0);
480
481	/* Enable all VOU module clocks */
482	zx_writel(vou->vouctl + VOU_CLK_EN, ~0);
483
484	/* Clear both OSD and TIMING_CTRL interrupt state */
485	zx_writel(vou->osd + OSD_INT_CLRSTA, ~0);
486	zx_writel(vou->timing + TIMING_INT_STATE, ~0);
487
488	/* Enable OSD and TIMING_CTRL interrrupts */
489	zx_writel(vou->osd + OSD_INT_MSK, OSD_INT_ENABLE);
490	zx_writel(vou->timing + TIMING_INT_CTRL, TIMING_INT_ENABLE);
491
492	/* Select GPC as input to gl/vl scaler as a sane default setting */
493	zx_writel(vou->otfppu + OTFPPU_RSZ_DATA_SOURCE, 0x2a);
494
495	/*
496	 * Needs to reset channel and layer logic per frame when frame starts
497	 * to get VOU work properly.
498	 */
499	zx_writel_mask(vou->osd + OSD_RST_CLR, RST_PER_FRAME, RST_PER_FRAME);
500
501	vou_dtrc_init(vou);
502}
503
504static int zx_crtc_bind(struct device *dev, struct device *master, void *data)
505{
506	struct platform_device *pdev = to_platform_device(dev);
507	struct drm_device *drm = data;
508	struct zx_vou_hw *vou;
509	struct resource *res;
510	int irq;
511	int ret;
512
513	vou = devm_kzalloc(dev, sizeof(*vou), GFP_KERNEL);
514	if (!vou)
515		return -ENOMEM;
516
517	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "osd");
518	vou->osd = devm_ioremap_resource(dev, res);
519	if (IS_ERR(vou->osd)) {
520		ret = PTR_ERR(vou->osd);
521		DRM_DEV_ERROR(dev, "failed to remap osd region: %d\n", ret);
522		return ret;
523	}
524
525	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "timing_ctrl");
526	vou->timing = devm_ioremap_resource(dev, res);
527	if (IS_ERR(vou->timing)) {
528		ret = PTR_ERR(vou->timing);
529		DRM_DEV_ERROR(dev, "failed to remap timing_ctrl region: %d\n",
530			      ret);
531		return ret;
532	}
533
534	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dtrc");
535	vou->dtrc = devm_ioremap_resource(dev, res);
536	if (IS_ERR(vou->dtrc)) {
537		ret = PTR_ERR(vou->dtrc);
538		DRM_DEV_ERROR(dev, "failed to remap dtrc region: %d\n", ret);
539		return ret;
540	}
541
542	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vou_ctrl");
543	vou->vouctl = devm_ioremap_resource(dev, res);
544	if (IS_ERR(vou->vouctl)) {
545		ret = PTR_ERR(vou->vouctl);
546		DRM_DEV_ERROR(dev, "failed to remap vou_ctrl region: %d\n",
547			      ret);
548		return ret;
549	}
550
551	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otfppu");
552	vou->otfppu = devm_ioremap_resource(dev, res);
553	if (IS_ERR(vou->otfppu)) {
554		ret = PTR_ERR(vou->otfppu);
555		DRM_DEV_ERROR(dev, "failed to remap otfppu region: %d\n", ret);
556		return ret;
557	}
558
559	irq = platform_get_irq(pdev, 0);
560	if (irq < 0)
561		return irq;
562
563	vou->axi_clk = devm_clk_get(dev, "aclk");
564	if (IS_ERR(vou->axi_clk)) {
565		ret = PTR_ERR(vou->axi_clk);
566		DRM_DEV_ERROR(dev, "failed to get axi_clk: %d\n", ret);
567		return ret;
568	}
569
570	vou->ppu_clk = devm_clk_get(dev, "ppu_wclk");
571	if (IS_ERR(vou->ppu_clk)) {
572		ret = PTR_ERR(vou->ppu_clk);
573		DRM_DEV_ERROR(dev, "failed to get ppu_clk: %d\n", ret);
574		return ret;
575	}
576
577	ret = clk_prepare_enable(vou->axi_clk);
578	if (ret) {
579		DRM_DEV_ERROR(dev, "failed to enable axi_clk: %d\n", ret);
580		return ret;
581	}
582
583	clk_prepare_enable(vou->ppu_clk);
584	if (ret) {
585		DRM_DEV_ERROR(dev, "failed to enable ppu_clk: %d\n", ret);
586		goto disable_axi_clk;
587	}
588
589	vou->dev = dev;
590	dev_set_drvdata(dev, vou);
591
592	vou_hw_init(vou);
593
594	ret = devm_request_irq(dev, irq, vou_irq_handler, 0, "zx_vou", vou);
595	if (ret < 0) {
596		DRM_DEV_ERROR(dev, "failed to request vou irq: %d\n", ret);
597		goto disable_ppu_clk;
598	}
599
600	ret = zx_crtc_init(drm, vou, VOU_CHN_MAIN);
601	if (ret) {
602		DRM_DEV_ERROR(dev, "failed to init main channel crtc: %d\n",
603			      ret);
604		goto disable_ppu_clk;
605	}
606
607	ret = zx_crtc_init(drm, vou, VOU_CHN_AUX);
608	if (ret) {
609		DRM_DEV_ERROR(dev, "failed to init aux channel crtc: %d\n",
610			      ret);
611		goto disable_ppu_clk;
612	}
613
614	return 0;
615
616disable_ppu_clk:
617	clk_disable_unprepare(vou->ppu_clk);
618disable_axi_clk:
619	clk_disable_unprepare(vou->axi_clk);
620	return ret;
621}
622
623static void zx_crtc_unbind(struct device *dev, struct device *master,
624			   void *data)
625{
626	struct zx_vou_hw *vou = dev_get_drvdata(dev);
627
628	clk_disable_unprepare(vou->axi_clk);
629	clk_disable_unprepare(vou->ppu_clk);
630}
631
632static const struct component_ops zx_crtc_component_ops = {
633	.bind = zx_crtc_bind,
634	.unbind = zx_crtc_unbind,
635};
636
637static int zx_crtc_probe(struct platform_device *pdev)
638{
639	return component_add(&pdev->dev, &zx_crtc_component_ops);
640}
641
642static int zx_crtc_remove(struct platform_device *pdev)
643{
644	component_del(&pdev->dev, &zx_crtc_component_ops);
645	return 0;
646}
647
648static const struct of_device_id zx_crtc_of_match[] = {
649	{ .compatible = "zte,zx296718-dpc", },
650	{ /* end */ },
651};
652MODULE_DEVICE_TABLE(of, zx_crtc_of_match);
653
654struct platform_driver zx_crtc_driver = {
655	.probe = zx_crtc_probe,
656	.remove = zx_crtc_remove,
657	.driver	= {
658		.name = "zx-crtc",
659		.of_match_table	= zx_crtc_of_match,
660	},
661};