2 * drivers/amlogic/drm/meson_vpu.c
4 * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
19 #include <drm/drm_plane.h>
20 #include <drm/drm_atomic.h>
21 #include <drm/drm_atomic_helper.h>
22 #include <drm/drm_fb_cma_helper.h>
23 #include <drm/drm_gem_cma_helper.h>
25 #include <linux/platform_device.h>
26 #include <linux/of_device.h>
28 #include <linux/component.h>
29 #include <linux/of_reserved_mem.h>
30 #include <linux/dma-contiguous.h>
31 #include <linux/cma.h>
32 #ifdef CONFIG_DRM_MESON_USE_ION
33 #include <ion/ion_priv.h>
37 #include <linux/amlogic/media/vout/vout_notify.h>
38 #ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT
39 #include <linux/amlogic/media/amvecm/amvecm.h>
43 #ifdef CONFIG_DRM_MESON_USE_ION
46 #include "meson_vpu.h"
47 #include "meson_plane.h"
48 #include "meson_crtc.h"
49 #include "meson_vpu_pipeline.h"
51 struct vpu_device_data_s {
52 enum cpuid_type_e cpu_id;
53 enum osd_ver_e osd_ver;
54 enum osd_afbc_e afbc_type;
67 static struct am_vout_mode am_vout_modes[] = {
68 { "1080p60hz", VMODE_HDMI, 1920, 1080, 60, 0},
69 { "1080p30hz", VMODE_HDMI, 1920, 1080, 30, 0},
70 { "1080p50hz", VMODE_HDMI, 1920, 1080, 50, 0},
71 { "1080p25hz", VMODE_HDMI, 1920, 1080, 25, 0},
72 { "1080p24hz", VMODE_HDMI, 1920, 1080, 24, 0},
73 { "2160p30hz", VMODE_HDMI, 3840, 2160, 30, 0},
74 { "2160p60hz", VMODE_HDMI, 3840, 2160, 60, 0},
75 { "2160p50hz", VMODE_HDMI, 3840, 2160, 50, 0},
76 { "2160p25hz", VMODE_HDMI, 3840, 2160, 25, 0},
77 { "2160p24hz", VMODE_HDMI, 3840, 2160, 24, 0},
78 { "1080i60hz", VMODE_HDMI, 1920, 1080, 60, DRM_MODE_FLAG_INTERLACE},
79 { "1080i50hz", VMODE_HDMI, 1920, 1080, 50, DRM_MODE_FLAG_INTERLACE},
80 { "720p60hz", VMODE_HDMI, 1280, 720, 60, 0},
81 { "720p50hz", VMODE_HDMI, 1280, 720, 50, 0},
82 { "480p60hz", VMODE_HDMI, 720, 480, 60, 0},
83 { "480i60hz", VMODE_HDMI, 720, 480, 60, DRM_MODE_FLAG_INTERLACE},
84 { "576p50hz", VMODE_HDMI, 720, 576, 50, 0},
85 { "576i50hz", VMODE_HDMI, 720, 576, 50, DRM_MODE_FLAG_INTERLACE},
86 { "480p60hz", VMODE_HDMI, 720, 480, 60, 0},
90 static struct osd_device_data_s osd_gxbb = {
91 .cpu_id = __MESON_CPU_MAJOR_ID_GXBB,
92 .osd_ver = OSD_NORMAL,
98 .has_dolby_vision = 0,
100 .vpp_fifo_len = 0x77f,
101 .dummy_data = 0x00808000,
105 static struct osd_device_data_s osd_gxl = {
106 .cpu_id = __MESON_CPU_MAJOR_ID_GXL,
107 .osd_ver = OSD_NORMAL,
108 .afbc_type = NO_AFBC,
113 .has_dolby_vision = 0,
115 .vpp_fifo_len = 0x77f,
116 .dummy_data = 0x00808000,
120 static struct osd_device_data_s osd_gxm = {
121 .cpu_id = __MESON_CPU_MAJOR_ID_GXM,
122 .osd_ver = OSD_NORMAL,
123 .afbc_type = MESON_AFBC,
128 .has_dolby_vision = 0,
130 .vpp_fifo_len = 0xfff,
131 .dummy_data = 0x00202000,/* dummy data is different */
135 static struct osd_device_data_s osd_txl = {
136 .cpu_id = __MESON_CPU_MAJOR_ID_TXL,
137 .osd_ver = OSD_NORMAL,
138 .afbc_type = NO_AFBC,
143 .has_dolby_vision = 0,
145 .vpp_fifo_len = 0x77f,
146 .dummy_data = 0x00808000,
150 static struct osd_device_data_s osd_txlx = {
151 .cpu_id = __MESON_CPU_MAJOR_ID_TXLX,
152 .osd_ver = OSD_NORMAL,
153 .afbc_type = NO_AFBC,
158 .has_dolby_vision = 1,
159 .osd_fifo_len = 64, /* fifo len 64*8 = 512 */
160 .vpp_fifo_len = 0x77f,
161 .dummy_data = 0x00808000,
165 static struct osd_device_data_s osd_axg = {
166 .cpu_id = __MESON_CPU_MAJOR_ID_AXG,
167 .osd_ver = OSD_SIMPLE,
168 .afbc_type = NO_AFBC,
173 .has_dolby_vision = 0,
174 /* use iomap its self, no rdma, no canvas, no freescale */
175 .osd_fifo_len = 64, /* fifo len 64*8 = 512 */
176 .vpp_fifo_len = 0x400,
177 .dummy_data = 0x00808000,
181 static struct osd_device_data_s osd_g12a = {
182 .cpu_id = __MESON_CPU_MAJOR_ID_G12A,
183 .osd_ver = OSD_HIGH_ONE,
184 .afbc_type = MALI_AFBC,
189 .has_dolby_vision = 0,
190 .osd_fifo_len = 64, /* fifo len 64*8 = 512 */
191 .vpp_fifo_len = 0xfff,/* 2048 */
192 .dummy_data = 0x00808000,
196 static struct osd_device_data_s osd_g12b = {
197 .cpu_id = __MESON_CPU_MAJOR_ID_G12B,
198 .osd_ver = OSD_HIGH_ONE,
199 .afbc_type = MALI_AFBC,
204 .has_dolby_vision = 0,
205 .osd_fifo_len = 64, /* fifo len 64*8 = 512 */
206 .vpp_fifo_len = 0xfff,/* 2048 */
207 .dummy_data = 0x00808000,
211 static struct osd_device_data_s osd_tl1 = {
212 .cpu_id = __MESON_CPU_MAJOR_ID_TL1,
213 .osd_ver = OSD_HIGH_ONE,
214 .afbc_type = MALI_AFBC,
219 .has_dolby_vision = 0,
220 .osd_fifo_len = 64, /* fifo len 64*8 = 512 */
221 .vpp_fifo_len = 0xfff,/* 2048 */
222 .dummy_data = 0x00808000,
224 .osd0_sc_independ = 0,
227 static struct osd_device_data_s osd_sm1 = {
228 .cpu_id = __MESON_CPU_MAJOR_ID_SM1,
229 .osd_ver = OSD_HIGH_ONE,
230 .afbc_type = MALI_AFBC,
235 .has_dolby_vision = 1,
236 .osd_fifo_len = 64, /* fifo len 64*8 = 512 */
237 .vpp_fifo_len = 0xfff,/* 2048 */
238 .dummy_data = 0x00808000,
240 .osd0_sc_independ = 0,
243 static struct osd_device_data_s osd_tm2 = {
244 .cpu_id = __MESON_CPU_MAJOR_ID_TM2,
245 .osd_ver = OSD_HIGH_ONE,
246 .afbc_type = MALI_AFBC,
251 .has_dolby_vision = 1,
252 .osd_fifo_len = 64, /* fifo len 64*8 = 512 */
253 .vpp_fifo_len = 0xfff,/* 2048 */
254 .dummy_data = 0x00808000,
256 .osd0_sc_independ = 1,
258 struct osd_device_data_s osd_meson_dev;
259 static u32 logo_memsize;
260 static struct page *logo_page;
261 static struct delayed_work osd_dwork;
262 static struct platform_device *gp_dev;
263 static unsigned long gem_mem_start, gem_mem_size;
265 int am_meson_crtc_dts_info_set(const void *dt_match_data)
267 struct osd_device_data_s *osd_meson;
269 osd_meson = (struct osd_device_data_s *)dt_match_data;
271 memcpy(&osd_meson_dev, osd_meson,
272 sizeof(struct osd_device_data_s));
273 osd_meson_dev.viu1_osd_count = osd_meson_dev.osd_count;
274 if (osd_meson_dev.has_viu2) {
275 /* set viu1 osd count */
276 osd_meson_dev.viu1_osd_count--;
277 osd_meson_dev.viu2_index = osd_meson_dev.viu1_osd_count;
280 DRM_ERROR("%s data NOT match\n", __func__);
287 static int am_meson_crtc_loader_protect(struct drm_crtc *crtc, bool on)
289 struct am_meson_crtc *amcrtc = to_am_meson_crtc(crtc);
291 DRM_INFO("%s %d\n", __func__, on);
294 enable_irq(amcrtc->irq);
295 drm_crtc_vblank_on(crtc);
297 disable_irq(amcrtc->irq);
298 drm_crtc_vblank_off(crtc);
304 static int am_meson_crtc_enable_vblank(struct drm_crtc *crtc)
309 static void am_meson_crtc_disable_vblank(struct drm_crtc *crtc)
313 const struct meson_crtc_funcs meson_private_crtc_funcs = {
314 .loader_protect = am_meson_crtc_loader_protect,
315 .enable_vblank = am_meson_crtc_enable_vblank,
316 .disable_vblank = am_meson_crtc_disable_vblank,
319 char *am_meson_crtc_get_voutmode(struct drm_display_mode *mode)
322 struct vinfo_s *vinfo;
324 vinfo = get_current_vinfo();
326 if (vinfo && vinfo->mode == VMODE_LCD)
329 for (i = 0; i < ARRAY_SIZE(am_vout_modes); i++) {
330 if (am_vout_modes[i].width == mode->hdisplay &&
331 am_vout_modes[i].height == mode->vdisplay &&
332 am_vout_modes[i].vrefresh == mode->vrefresh &&
333 am_vout_modes[i].flags ==
334 (mode->flags & DRM_MODE_FLAG_INTERLACE))
335 return am_vout_modes[i].name;
340 void am_meson_crtc_handle_vsync(struct am_meson_crtc *amcrtc)
343 struct drm_crtc *crtc;
345 crtc = &amcrtc->base;
346 drm_crtc_handle_vblank(crtc);
348 spin_lock_irqsave(&crtc->dev->event_lock, flags);
350 drm_crtc_send_vblank_event(crtc, amcrtc->event);
351 drm_crtc_vblank_put(crtc);
352 amcrtc->event = NULL;
354 spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
357 void am_meson_crtc_irq(struct meson_drm *priv)
359 struct am_meson_crtc *amcrtc = to_am_meson_crtc(priv->crtc);
361 am_meson_crtc_handle_vsync(amcrtc);
364 static irqreturn_t am_meson_vpu_irq(int irq, void *arg)
366 struct drm_device *dev = arg;
367 struct meson_drm *priv = dev->dev_private;
369 am_meson_crtc_irq(priv);
374 static void mem_free_work(struct work_struct *work)
376 if (logo_memsize > 0) {
378 pr_info("%s, free memory: addr:0x%x\n",
379 __func__, logo_memsize);
381 dma_release_from_contiguous(&gp_dev->dev,
383 logo_memsize >> PAGE_SHIFT);
388 static int am_meson_vpu_bind(struct device *dev,
389 struct device *master, void *data)
391 struct platform_device *pdev = to_platform_device(dev);
392 struct drm_device *drm_dev = data;
393 struct meson_drm *private = drm_dev->dev_private;
394 struct meson_vpu_pipeline *pipeline = private->pipeline;
395 struct am_meson_crtc *amcrtc;
401 /* Allocate crtc struct */
402 pr_info("[%s] in\n", __func__);
403 amcrtc = devm_kzalloc(dev, sizeof(*amcrtc),
408 amcrtc->priv = private;
410 amcrtc->drm_dev = drm_dev;
412 dev_set_drvdata(dev, amcrtc);
414 /* init reserved memory */
415 ret = of_reserved_mem_device_init(&pdev->dev);
417 dev_err(dev, "failed to init reserved memory\n");
420 cma = dev_get_cma_area(&pdev->dev);
422 logo_memsize = cma_get_size(cma);
423 pr_info("reserved memory base:0x%x, size:0x%x\n",
424 (u32)cma_get_base(cma), logo_memsize);
425 if (logo_memsize > 0) {
427 dma_alloc_from_contiguous(&pdev->dev,
432 pr_err("allocate buffer failed:%d\n",
437 pr_info("------ NO CMA\n");
441 dma_declare_coherent_memory(drm_dev->dev, gem_mem_start,
442 gem_mem_start, gem_mem_size,
443 DMA_MEMORY_EXCLUSIVE);
444 pr_info("meson drm mem_start = 0x%x, size = 0x%x\n",
445 (u32)gem_mem_start, (u32)gem_mem_size);
448 ret = am_meson_plane_create(private);
452 ret = am_meson_crtc_create(amcrtc);
456 am_meson_register_crtc_funcs(private->crtc, &meson_private_crtc_funcs);
458 ret = of_property_read_u8(dev->of_node,
459 "osd_ver", &pipeline->osd_version);
460 vpu_pipeline_init(pipeline);
463 irq = platform_get_irq(pdev, 0);
465 dev_err(dev, "cannot find irq for vpu\n");
468 amcrtc->irq = (unsigned int)irq;
470 ret = devm_request_irq(dev, amcrtc->irq, am_meson_vpu_irq,
471 IRQF_SHARED, dev_name(dev), drm_dev);
475 /* IRQ is initially disabled; it gets enabled in crtc_enable */
476 disable_irq(amcrtc->irq);
478 INIT_DELAYED_WORK(&osd_dwork, mem_free_work);
479 schedule_delayed_work(&osd_dwork, msecs_to_jiffies(60 * 1000));
480 pr_info("[%s] out\n", __func__);
484 static void am_meson_vpu_unbind(struct device *dev,
485 struct device *master, void *data)
487 struct drm_device *drm_dev = data;
488 struct meson_drm *private = drm_dev->dev_private;
490 am_meson_unregister_crtc_funcs(private->crtc);
491 #ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT
492 amvecm_drm_gamma_disable(0);
493 am_meson_ctm_disable();
495 osd_drm_debugfs_exit();
498 static const struct component_ops am_meson_vpu_component_ops = {
499 .bind = am_meson_vpu_bind,
500 .unbind = am_meson_vpu_unbind,
503 static const struct of_device_id am_meson_vpu_driver_dt_match[] = {
504 { .compatible = "amlogic,meson-gxbb-vpu",
505 .data = &osd_gxbb, },
506 { .compatible = "amlogic,meson-gxl-vpu",
508 { .compatible = "amlogic,meson-gxm-vpu",
510 { .compatible = "amlogic,meson-txl-vpu",
512 { .compatible = "amlogic,meson-txlx-vpu",
513 .data = &osd_txlx, },
514 { .compatible = "amlogic,meson-axg-vpu",
516 { .compatible = "amlogic,meson-g12a-vpu",
517 .data = &osd_g12a, },
518 { .compatible = "amlogic,meson-g12b-vpu",
519 .data = &osd_g12b, },
520 {.compatible = "amlogic, meson-tl1-vpu",
522 {.compatible = "amlogic, meson-sm1-vpu",
524 {.compatible = "amlogic, meson-tm2-vpu",
529 MODULE_DEVICE_TABLE(of, am_meson_vpu_driver_dt_match);
531 static int am_meson_vpu_probe(struct platform_device *pdev)
533 struct device *dev = &pdev->dev;
534 const void *vpu_data;
537 pr_info("[%s] in\n", __func__);
539 dev_err(dev, "can't find vpu devices\n");
543 vpu_data = of_device_get_match_data(dev);
545 ret = am_meson_crtc_dts_info_set(vpu_data);
549 dev_err(dev, "%s NOT match\n", __func__);
552 pr_info("[%s] out\n", __func__);
553 return component_add(dev, &am_meson_vpu_component_ops);
556 static int am_meson_vpu_remove(struct platform_device *pdev)
558 component_del(&pdev->dev, &am_meson_vpu_component_ops);
563 static struct platform_driver am_meson_vpu_platform_driver = {
564 .probe = am_meson_vpu_probe,
565 .remove = am_meson_vpu_remove,
568 .owner = THIS_MODULE,
569 .of_match_table = of_match_ptr(am_meson_vpu_driver_dt_match),
573 static int gem_mem_device_init(struct reserved_mem *rmem, struct device *dev)
578 pr_info("Can't get reverse mem!\n");
582 gem_mem_start = rmem->base;
583 gem_mem_size = rmem->size;
584 pr_info("init gem memsource addr:0x%x size:0x%x\n",
585 (u32)gem_mem_start, (u32)gem_mem_size);
590 static const struct reserved_mem_ops rmem_gem_ops = {
591 .device_init = gem_mem_device_init,
594 static int __init gem_mem_setup(struct reserved_mem *rmem)
596 rmem->ops = &rmem_gem_ops;
597 pr_info("gem mem setup\n");
601 RESERVEDMEM_OF_DECLARE(gem, "amlogic, gem_memory", gem_mem_setup);
603 module_platform_driver(am_meson_vpu_platform_driver);
605 MODULE_AUTHOR("MultiMedia Amlogic <multimedia-sh@amlogic.com>");
606 MODULE_DESCRIPTION("Amlogic Meson Drm VPU driver");
607 MODULE_LICENSE("GPL");