mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 19:30:30 +09:00
drm: add amlogic KMS implement
PD#152825: add bypass implement based on amlogic internal driver. We need reuse the existing driver to implement drm, but not fully re-write all the drm driver. Change-Id: Ie6492d8eb978059b9822b98cb7e34a643bdd2c6e Signed-off-by: sky zhou <sky.zhou@amlogic.com>
This commit is contained in:
@@ -14110,3 +14110,7 @@ M: Guosong Zhou <guosong.zhou@amlogic.com>
|
||||
F: drivers/amlogic/media/video_processor/Kconfig
|
||||
F: drivers/amlogic/media/video_processor/Makefile
|
||||
F: drivers/amlogic/media/video_processor/pic_dev/*
|
||||
|
||||
AMLOGIC DRM DRIVER
|
||||
M: Sky Zhou <sky.zhou@amlogic.com>
|
||||
F: driver/amlogic/drm/*
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
};
|
||||
ion_dev {
|
||||
compatible = "amlogic, ion_dev";
|
||||
memory-region = <&ion_reserved>;
|
||||
};
|
||||
memory@00000000 {
|
||||
device_type = "memory";
|
||||
@@ -61,12 +62,11 @@
|
||||
reg = <0x0 0x05300000 0x0 0x2000000>;
|
||||
no-map;
|
||||
};
|
||||
fb_reserved:linux,meson-fb {
|
||||
ion_reserved:linux,ion-dev {
|
||||
compatible = "shared-dma-pool";
|
||||
reusable;
|
||||
size = <0x0 0x2400000>;
|
||||
size = <0x0 0x8000000>;
|
||||
alignment = <0x0 0x400000>;
|
||||
alloc-ranges = <0x0 0x7dc00000 0x0 0x2400000>;
|
||||
};
|
||||
//don't put other dts in front of fb_reserved
|
||||
|
||||
@@ -692,7 +692,6 @@
|
||||
|
||||
meson-fb {
|
||||
compatible = "amlogic, meson-fb";
|
||||
memory-region = <&fb_reserved>;
|
||||
dev_name = "meson-fb";
|
||||
status = "okay";
|
||||
interrupts = <0 3 1
|
||||
|
||||
@@ -6,3 +6,31 @@ config DRM_MESON
|
||||
select DRM_GEM_CMA_HELPER
|
||||
select VIDEOMODE_HELPERS
|
||||
select REGMAP_MMIO
|
||||
|
||||
config DRM_MESON_BYPASS_MODE
|
||||
bool "use the drm display path, but all the implements in other drivers."
|
||||
default y
|
||||
depends on DRM_MESON
|
||||
help
|
||||
The MESON DRM want to rewrite all the display component,its a big job,
|
||||
and will meet a lot of bugs which are already handled before.
|
||||
We add bypass mode to implment drm based on amlogic existing driver,
|
||||
it should be easier and more robust.
|
||||
|
||||
config DRM_MESON_USE_ION
|
||||
bool "gem use ion to alloc/free graphic buffer."
|
||||
default y
|
||||
depends on DRM_MESON
|
||||
help
|
||||
MESON DRM use CMA HELPER to manage framebuffer.
|
||||
It need reserve memory in CMA pool.
|
||||
We implement GEM to allocate/free framebuffer from ion.
|
||||
For dumb used by displaycontrol we alloc from the ION CMA HEAP.
|
||||
For dumb used by app, we can alloc from the ION.
|
||||
SYSTEM HEAP which dont need reserve memory.
|
||||
|
||||
config DRM_MESON_EMULATE_FBDEV
|
||||
bool "emulate framebuffer dev by drm."
|
||||
default n
|
||||
help
|
||||
Emulate framebuffer device for device which need use fbdev api.
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
meson-y := meson_drv.o meson_gem.o meson_dmabuf.o
|
||||
#meson-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o meson_canvas.o meson_plane.o meson_crtc.o meson_venc_cvbs.o
|
||||
meson-y := meson_drv.o
|
||||
|
||||
ifeq ($(CONFIG_DRM_MESON_BYPASS_MODE),y)
|
||||
meson-y += am_meson_plane.o am_meson_crtc.o am_meson_hdmi.o
|
||||
ccflags-y += -Idrivers/amlogic/media/osd/
|
||||
else
|
||||
meson-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o meson_canvas.o meson_plane.o meson_crtc.o meson_venc_cvbs.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_DRM_MESON_USE_ION),y)
|
||||
meson-y += am_meson_gem.o am_meson_fb.o
|
||||
ccflags-y += -Idrivers/staging/android/
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_DRM_MESON) += meson.o
|
||||
ccflags-y += -Idrivers/staging/android/
|
||||
|
||||
247
drivers/amlogic/drm/am_meson_crtc.c
Normal file
247
drivers/amlogic/drm/am_meson_crtc.c
Normal file
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* drivers/amlogic/drm/am_meson_crtc.c
|
||||
*
|
||||
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_plane.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
|
||||
/* Amlogic Headers */
|
||||
#include <linux/amlogic/media/vout/vout_notify.h>
|
||||
|
||||
#include "meson_crtc.h"
|
||||
#include "osd_drm.h"
|
||||
|
||||
|
||||
#define to_am_meson_crtc(x) container_of(x, struct am_meson_crtc, base)
|
||||
|
||||
struct am_vout_mode {
|
||||
char name[DRM_DISPLAY_MODE_LEN];
|
||||
enum vmode_e mode;
|
||||
int width, height, vrefresh;
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
static struct am_vout_mode am_vout_modes[] = {
|
||||
{ "1080p60hz", VMODE_HDMI, 1920, 1080, 60, 0},
|
||||
{ "1080p30hz", VMODE_HDMI, 1920, 1080, 30, 0},
|
||||
{ "1080p50hz", VMODE_HDMI, 1920, 1080, 50, 0},
|
||||
{ "1080p25hz", VMODE_HDMI, 1920, 1080, 25, 0},
|
||||
{ "1080p24hz", VMODE_HDMI, 1920, 1080, 24, 0},
|
||||
{ "2160p30hz", VMODE_HDMI, 3840, 2160, 30, 0},
|
||||
{ "2160p60hz", VMODE_HDMI, 3840, 2160, 60, 0},
|
||||
{ "2160p50hz", VMODE_HDMI, 3840, 2160, 50, 0},
|
||||
{ "2160p25hz", VMODE_HDMI, 3840, 2160, 25, 0},
|
||||
{ "2160p24hz", VMODE_HDMI, 3840, 2160, 24, 0},
|
||||
{ "1080i60hz", VMODE_HDMI, 1920, 1080, 60, DRM_MODE_FLAG_INTERLACE},
|
||||
{ "1080i50hz", VMODE_HDMI, 1920, 1080, 50, DRM_MODE_FLAG_INTERLACE},
|
||||
{ "720p60hz", VMODE_HDMI, 1280, 720, 60, 0},
|
||||
{ "720p50hz", VMODE_HDMI, 1280, 720, 50, 0},
|
||||
{ "480p60hz", VMODE_HDMI, 720, 480, 60, 0},
|
||||
{ "480i60hz", VMODE_HDMI, 720, 480, 60, DRM_MODE_FLAG_INTERLACE},
|
||||
{ "576p50hz", VMODE_HDMI, 720, 576, 50, 0},
|
||||
{ "576i50hz", VMODE_HDMI, 720, 576, 50, DRM_MODE_FLAG_INTERLACE},
|
||||
{ "480p60hz", VMODE_HDMI, 720, 480, 60, 0},
|
||||
};
|
||||
|
||||
char *am_meson_crtc_get_voutmode(struct drm_display_mode *mode)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(am_vout_modes); i++) {
|
||||
if ((am_vout_modes[i].width == mode->hdisplay)
|
||||
&& (am_vout_modes[i].height == mode->vdisplay)
|
||||
&& (am_vout_modes[i].vrefresh == mode->vrefresh)
|
||||
&& (am_vout_modes[i].flags ==
|
||||
(mode->flags&DRM_MODE_FLAG_INTERLACE)))
|
||||
return am_vout_modes[i].name;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct am_vpp {
|
||||
};
|
||||
|
||||
struct am_vout {
|
||||
};
|
||||
|
||||
struct am_meson_crtc {
|
||||
struct drm_crtc base;
|
||||
struct meson_drm *priv;
|
||||
|
||||
struct drm_pending_vblank_event *event;
|
||||
|
||||
struct am_vpp vpp;
|
||||
struct am_vout vout;
|
||||
};
|
||||
|
||||
void am_meson_crtc_handle_vsync(struct am_meson_crtc *amcrtc)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
crtc = &amcrtc->base;
|
||||
drm_crtc_handle_vblank(crtc);
|
||||
|
||||
spin_lock_irqsave(&crtc->dev->event_lock, flags);
|
||||
if (amcrtc->event) {
|
||||
drm_crtc_send_vblank_event(crtc, amcrtc->event);
|
||||
drm_crtc_vblank_put(crtc);
|
||||
amcrtc->event = NULL;
|
||||
}
|
||||
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
|
||||
}
|
||||
|
||||
int am_meson_crtc_set_mode(struct drm_mode_set *set)
|
||||
{
|
||||
struct am_meson_crtc *amcrtc;
|
||||
int ret;
|
||||
|
||||
DRM_DEBUG_DRIVER("am_crtc_set_mode\n");
|
||||
|
||||
amcrtc = to_am_meson_crtc(set->crtc);
|
||||
ret = drm_atomic_helper_set_config(set);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct drm_crtc_funcs am_meson_crtc_funcs = {
|
||||
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
|
||||
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
|
||||
.destroy = drm_crtc_cleanup,
|
||||
.page_flip = drm_atomic_helper_page_flip,
|
||||
.reset = drm_atomic_helper_crtc_reset,
|
||||
.set_config = am_meson_crtc_set_mode,
|
||||
};
|
||||
|
||||
static bool am_meson_crtc_mode_fixup(struct drm_crtc *crtc,
|
||||
const struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adj_mode)
|
||||
{
|
||||
//DRM_INFO("am_meson_crtc_mode_fixup !!\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void am_meson_crtc_enable(struct drm_crtc *crtc)
|
||||
{
|
||||
enum vmode_e mode;
|
||||
int ret = 0;
|
||||
char *name;
|
||||
struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
|
||||
|
||||
if (!adjusted_mode) {
|
||||
DRM_ERROR("meson_crtc_enable fail, unsupport mode:%s\n",
|
||||
adjusted_mode->name);
|
||||
return;
|
||||
}
|
||||
//DRM_INFO("meson_crtc_enable %s\n", adjusted_mode->name);
|
||||
name = am_meson_crtc_get_voutmode(adjusted_mode);
|
||||
|
||||
mode = validate_vmode(name);
|
||||
if (mode == VMODE_MAX) {
|
||||
DRM_ERROR("no matched vout mode\n");
|
||||
return;
|
||||
}
|
||||
|
||||
vout_notifier_call_chain(VOUT_EVENT_MODE_CHANGE_PRE, &mode);
|
||||
ret = set_current_vmode(mode);
|
||||
if (ret)
|
||||
DRM_ERROR("new mode %s set error\n", name);
|
||||
else
|
||||
DRM_INFO("new mode %s set ok\n", name);
|
||||
vout_notifier_call_chain(VOUT_EVENT_MODE_CHANGE, &mode);
|
||||
}
|
||||
|
||||
void am_meson_crtc_disable(struct drm_crtc *crtc)
|
||||
{
|
||||
//DRM_INFO("meson_crtc_disable!!\n");
|
||||
}
|
||||
|
||||
void am_meson_crtc_commit(struct drm_crtc *crtc)
|
||||
{
|
||||
//DRM_INFO("am_meson_crtc_commit!!\n");
|
||||
}
|
||||
|
||||
void am_meson_crtc_atomic_begin(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old_crtc_state)
|
||||
{
|
||||
struct am_meson_crtc *amcrtc;
|
||||
unsigned long flags;
|
||||
|
||||
amcrtc = to_am_meson_crtc(crtc);
|
||||
|
||||
if (crtc->state->event) {
|
||||
WARN_ON(drm_crtc_vblank_get(crtc) != 0);
|
||||
|
||||
spin_lock_irqsave(&crtc->dev->event_lock, flags);
|
||||
amcrtc->event = crtc->state->event;
|
||||
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
|
||||
crtc->state->event = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void am_meson_crtc_atomic_flush(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old_crtc_state)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct drm_crtc_helper_funcs am_crtc_helper_funcs = {
|
||||
.enable = am_meson_crtc_enable,
|
||||
.disable = am_meson_crtc_disable,
|
||||
.commit = am_meson_crtc_commit,
|
||||
.mode_fixup = am_meson_crtc_mode_fixup,
|
||||
.atomic_begin = am_meson_crtc_atomic_begin,
|
||||
.atomic_flush = am_meson_crtc_atomic_flush,
|
||||
};
|
||||
|
||||
int meson_crtc_create(struct meson_drm *priv)
|
||||
{
|
||||
struct am_meson_crtc *amcrtc;
|
||||
struct drm_crtc *crtc;
|
||||
int ret;
|
||||
|
||||
DRM_DEBUG("amlogic meson_crtc_create\n");
|
||||
amcrtc = devm_kzalloc(priv->drm->dev, sizeof(*amcrtc),
|
||||
GFP_KERNEL);
|
||||
if (!amcrtc)
|
||||
return -ENOMEM;
|
||||
|
||||
amcrtc->priv = priv;
|
||||
crtc = &amcrtc->base;
|
||||
ret = drm_crtc_init_with_planes(priv->drm, crtc,
|
||||
priv->primary_plane, priv->cursor_plane,
|
||||
&am_meson_crtc_funcs, "amlogic vpu");
|
||||
if (ret) {
|
||||
dev_err(priv->drm->dev, "Failed to init CRTC\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_crtc_helper_add(crtc, &am_crtc_helper_funcs);
|
||||
osd_drm_init();
|
||||
|
||||
priv->crtc = crtc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void meson_crtc_irq(struct meson_drm *priv)
|
||||
{
|
||||
struct am_meson_crtc *amcrtc = to_am_meson_crtc(priv->crtc);
|
||||
|
||||
osd_drm_vsync_isr_handler();
|
||||
am_meson_crtc_handle_vsync(amcrtc);
|
||||
}
|
||||
|
||||
80
drivers/amlogic/drm/am_meson_fb.c
Normal file
80
drivers/amlogic/drm/am_meson_fb.c
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* drivers/amlogic/drm/am_meson_fb.c
|
||||
*
|
||||
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
#include "am_meson_fb.h"
|
||||
|
||||
#define to_am_meson_fb(x) container_of(x, struct am_meson_fb, base)
|
||||
|
||||
void am_meson_fb_destroy(struct drm_framebuffer *framebuffer)
|
||||
{
|
||||
struct am_meson_fb *meson_fb = to_am_meson_fb(framebuffer);
|
||||
|
||||
drm_gem_object_unreference_unlocked(&meson_fb->bufp->base);
|
||||
drm_framebuffer_cleanup(framebuffer);
|
||||
kfree(meson_fb);
|
||||
}
|
||||
|
||||
int am_meson_fb_create_handle(struct drm_framebuffer *fb,
|
||||
struct drm_file *file_priv,
|
||||
unsigned int *handle)
|
||||
{
|
||||
DRM_INFO("ERROR: am_meson_fb_create_handle NOT implement.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct drm_framebuffer_funcs am_meson_fb_funcs = {
|
||||
.create_handle = am_meson_fb_create_handle, //must for fbdev emulate
|
||||
.destroy = am_meson_fb_destroy,
|
||||
};
|
||||
|
||||
struct drm_framebuffer *am_meson_fb_create(struct drm_device *dev,
|
||||
struct drm_file *file_priv,
|
||||
const struct drm_mode_fb_cmd2 *mode_cmd)
|
||||
{
|
||||
struct am_meson_fb *meson_fb = 0;
|
||||
struct drm_gem_object *obj = 0;
|
||||
struct am_meson_gem_object *meson_gem;
|
||||
int ret;
|
||||
|
||||
meson_fb = kzalloc(sizeof(*meson_fb), GFP_KERNEL);
|
||||
if (!meson_fb)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* only support one handle now.*/
|
||||
obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[0]);
|
||||
if (!obj) {
|
||||
dev_err(dev->dev, "Failed to lookup GEM handle\n");
|
||||
kfree(meson_fb);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
meson_gem = container_of(obj, struct am_meson_gem_object, base);
|
||||
meson_fb->bufp = meson_gem;
|
||||
|
||||
drm_helper_mode_fill_fb_struct(&meson_fb->base, mode_cmd);
|
||||
|
||||
ret = drm_framebuffer_init(dev, &meson_fb->base, &am_meson_fb_funcs);
|
||||
if (ret) {
|
||||
dev_err(dev->dev,
|
||||
"Failed to initialize framebuffer: %d\n",
|
||||
ret);
|
||||
drm_gem_object_unreference(obj);
|
||||
kfree(meson_fb);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return &meson_fb->base;
|
||||
}
|
||||
35
drivers/amlogic/drm/am_meson_fb.h
Normal file
35
drivers/amlogic/drm/am_meson_fb.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* drivers/amlogic/drm/am_meson_fb.h
|
||||
*
|
||||
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __AM_MESON_FB_H
|
||||
#define __AM_MESON_FB_H
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_gem.h>
|
||||
#include <drm/drm_framebuffer.h>
|
||||
#include <drm/drm_modeset_helper.h>
|
||||
|
||||
#include "am_meson_gem.h"
|
||||
|
||||
struct am_meson_fb {
|
||||
struct drm_framebuffer base;
|
||||
struct am_meson_gem_object *bufp;
|
||||
};
|
||||
|
||||
struct drm_framebuffer *am_meson_fb_create(struct drm_device *dev,
|
||||
struct drm_file *file_priv,
|
||||
const struct drm_mode_fb_cmd2 *mode_cmd);
|
||||
#endif
|
||||
497
drivers/amlogic/drm/am_meson_gem.c
Normal file
497
drivers/amlogic/drm/am_meson_gem.c
Normal file
@@ -0,0 +1,497 @@
|
||||
/*
|
||||
* drivers/amlogic/drm/am_meson_gem.c
|
||||
*
|
||||
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_gem.h>
|
||||
#include <drm/drm_vma_manager.h>
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/meson_ion.h>
|
||||
#include <ion/ion.h>
|
||||
|
||||
#include "meson_drv.h"
|
||||
#include "am_meson_gem.h"
|
||||
|
||||
#define to_am_meson_gem_obj(x) container_of(x, struct am_meson_gem_object, base)
|
||||
|
||||
static int am_meson_gem_alloc_ion_buff(
|
||||
struct ion_client *client,
|
||||
struct am_meson_gem_object *meson_gem_obj,
|
||||
int flags)
|
||||
{
|
||||
struct ion_handle *handle;
|
||||
bool bscatter = false;
|
||||
|
||||
if (!client)
|
||||
return -EINVAL;
|
||||
|
||||
if (!meson_gem_obj)
|
||||
return -EINVAL;
|
||||
|
||||
//TODO,check flags to set different ion heap type.
|
||||
handle = ion_alloc(client, meson_gem_obj->base.size,
|
||||
0, (1 << ION_HEAP_TYPE_DMA), 0);
|
||||
if (IS_ERR(handle)) {
|
||||
handle = ion_alloc(client, meson_gem_obj->base.size,
|
||||
0, (1 << ION_HEAP_TYPE_SYSTEM), 0);
|
||||
if (IS_ERR(handle)) {
|
||||
DRM_ERROR("am_meson_gem_alloc_ion_buff FAILED.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
bscatter = true;
|
||||
}
|
||||
|
||||
meson_gem_obj->handle = handle;
|
||||
meson_gem_obj->bscatter = bscatter;
|
||||
DRM_DEBUG("%s: allocate handle (%p).\n",
|
||||
__func__, meson_gem_obj->handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void am_meson_gem_free_ion_buf(
|
||||
struct drm_device *dev,
|
||||
struct am_meson_gem_object *meson_gem_obj)
|
||||
{
|
||||
struct ion_client *client = NULL;
|
||||
|
||||
if (meson_gem_obj->handle) {
|
||||
DRM_DEBUG("am_meson_gem_free_ion_buf free handle (%p).\n",
|
||||
meson_gem_obj->handle);
|
||||
client = meson_gem_obj->handle->client;
|
||||
ion_free(client, meson_gem_obj->handle);
|
||||
meson_gem_obj->handle = NULL;
|
||||
} else {
|
||||
DRM_ERROR("meson_gem_obj handle is null\n");
|
||||
}
|
||||
}
|
||||
|
||||
static struct am_meson_gem_object *am_meson_gem_object_create(
|
||||
struct drm_device *dev,
|
||||
unsigned int flags,
|
||||
unsigned long size,
|
||||
struct ion_client *client)
|
||||
{
|
||||
struct am_meson_gem_object *meson_gem_obj = NULL;
|
||||
int ret;
|
||||
|
||||
if (!size) {
|
||||
DRM_ERROR("invalid size.\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
meson_gem_obj = kzalloc(sizeof(*meson_gem_obj), GFP_KERNEL);
|
||||
if (!meson_gem_obj)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ret = drm_gem_object_init(dev, &meson_gem_obj->base, size);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("failed to initialize gem object\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = am_meson_gem_alloc_ion_buff(client, meson_gem_obj, flags);
|
||||
if (ret < 0) {
|
||||
drm_gem_object_release(&meson_gem_obj->base);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return meson_gem_obj;
|
||||
|
||||
error:
|
||||
kfree(meson_gem_obj);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
void am_meson_gem_object_free(struct drm_gem_object *obj)
|
||||
{
|
||||
struct am_meson_gem_object *meson_gem_obj = to_am_meson_gem_obj(obj);
|
||||
|
||||
DRM_DEBUG("am_meson_gem_object_free %p handle count = %d\n",
|
||||
meson_gem_obj, obj->handle_count);
|
||||
|
||||
if (obj->import_attach)
|
||||
drm_prime_gem_destroy(obj, meson_gem_obj->sgt);
|
||||
else
|
||||
am_meson_gem_free_ion_buf(obj->dev, meson_gem_obj);
|
||||
|
||||
drm_gem_free_mmap_offset(obj);
|
||||
|
||||
/* release file pointer to gem object. */
|
||||
drm_gem_object_release(obj);
|
||||
|
||||
kfree(meson_gem_obj);
|
||||
meson_gem_obj = NULL;
|
||||
}
|
||||
|
||||
int am_meson_gem_object_mmap(
|
||||
struct am_meson_gem_object *obj,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ion_buffer *buffer;
|
||||
|
||||
/*
|
||||
* Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the
|
||||
* vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map
|
||||
* the whole buffer.
|
||||
*/
|
||||
vma->vm_flags &= ~VM_PFNMAP;
|
||||
vma->vm_pgoff = 0;
|
||||
|
||||
if (obj->sgt) {
|
||||
DRM_ERROR("am_meson_gem_object_mmap imported buffer.\n");
|
||||
/*should NOT run to here.*/
|
||||
#if 0
|
||||
ret = dma_mmap_wc(obj->base.dev->dev, vma,
|
||||
page_address(sg_page(obj->sgt->sgl)),
|
||||
sg_dma_address(obj->sgt->sgl),
|
||||
vma->vm_end - vma->vm_start);
|
||||
#endif
|
||||
} else {
|
||||
buffer = obj->handle->buffer;
|
||||
|
||||
if (!buffer->heap->ops->map_user) {
|
||||
DRM_ERROR("%s:heap does not define map to userspace\n",
|
||||
__func__);
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
|
||||
if (!(buffer->flags & ION_FLAG_CACHED))
|
||||
vma->vm_page_prot =
|
||||
pgprot_writecombine(vma->vm_page_prot);
|
||||
|
||||
mutex_lock(&buffer->lock);
|
||||
/* now map it to userspace */
|
||||
ret = buffer->heap->ops->map_user(
|
||||
buffer->heap, buffer, vma);
|
||||
mutex_unlock(&buffer->lock);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
DRM_ERROR("%s: failure mapping buffer to userspace (%d)\n",
|
||||
__func__, ret);
|
||||
drm_gem_vm_close(vma);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int am_meson_gem_mmap(
|
||||
struct file *filp,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
struct drm_gem_object *obj;
|
||||
struct am_meson_gem_object *meson_gem_obj;
|
||||
int ret;
|
||||
|
||||
ret = drm_gem_mmap(filp, vma);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
obj = vma->vm_private_data;
|
||||
meson_gem_obj = to_am_meson_gem_obj(obj);
|
||||
DRM_ERROR("am_meson_gem_mmap %p.\n", meson_gem_obj);
|
||||
|
||||
ret = am_meson_gem_object_mmap(meson_gem_obj, vma);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int am_meson_gem_object_get_phyaddr(
|
||||
struct meson_drm *drm,
|
||||
struct am_meson_gem_object *meson_gem)
|
||||
{
|
||||
int addr;
|
||||
size_t len;
|
||||
|
||||
ion_phys(drm->gem_client, meson_gem->handle,
|
||||
(ion_phys_addr_t *)&addr, &len);
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
int am_meson_gem_dumb_create(
|
||||
struct drm_file *file_priv,
|
||||
struct drm_device *dev,
|
||||
struct drm_mode_create_dumb *args)
|
||||
{
|
||||
int ret = 0;
|
||||
struct am_meson_gem_object *meson_gem_obj;
|
||||
struct meson_drm *drmdrv = dev->dev_private;
|
||||
struct ion_client *client = (struct ion_client *)drmdrv->gem_client;
|
||||
int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
|
||||
|
||||
args->pitch = ALIGN(min_pitch, 64);
|
||||
if (args->size < args->pitch * args->height)
|
||||
args->size = args->pitch * args->height;
|
||||
|
||||
args->size = round_up(args->size, PAGE_SIZE);
|
||||
|
||||
meson_gem_obj = am_meson_gem_object_create(
|
||||
dev, args->flags, args->size, client);
|
||||
if (IS_ERR(meson_gem_obj))
|
||||
return PTR_ERR(meson_gem_obj);
|
||||
|
||||
/*
|
||||
* allocate a id of idr table where the obj is registered
|
||||
* and handle has the id what user can see.
|
||||
*/
|
||||
ret = drm_gem_handle_create(file_priv,
|
||||
&meson_gem_obj->base, &args->handle);
|
||||
/* drop reference from allocate - handle holds it now. */
|
||||
drm_gem_object_unreference_unlocked(&meson_gem_obj->base);
|
||||
if (ret) {
|
||||
DRM_ERROR("%s: create dumb handle failed %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
DRM_DEBUG("%s: create dumb %p with gem handle (0x%x)\n",
|
||||
__func__, meson_gem_obj, args->handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int am_meson_gem_dumb_destroy(
|
||||
struct drm_file *file,
|
||||
struct drm_device *dev,
|
||||
uint32_t handle)
|
||||
{
|
||||
DRM_DEBUG("%s: destroy dumb with handle (0x%x)\n", __func__, handle);
|
||||
drm_gem_handle_delete(file, handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int am_meson_gem_dumb_map_offset(
|
||||
struct drm_file *file_priv,
|
||||
struct drm_device *dev,
|
||||
uint32_t handle,
|
||||
uint64_t *offset)
|
||||
{
|
||||
struct drm_gem_object *obj;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
|
||||
/*
|
||||
* get offset of memory allocated for drm framebuffer.
|
||||
* - this callback would be called by user application
|
||||
* with DRM_IOCTL_MODE_MAP_DUMB command.
|
||||
*/
|
||||
obj = drm_gem_object_lookup(file_priv, handle);
|
||||
if (!obj) {
|
||||
DRM_ERROR("failed to lookup gem object.\n");
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = drm_gem_create_mmap_offset(obj);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
*offset = drm_vma_node_offset_addr(&obj->vma_node);
|
||||
DRM_DEBUG("offset = 0x%lx\n", (unsigned long)*offset);
|
||||
|
||||
out:
|
||||
drm_gem_object_unreference(obj);
|
||||
unlock:
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int am_meson_gem_create(struct meson_drm *drmdrv)
|
||||
{
|
||||
drmdrv->gem_client = meson_ion_client_create(-1, "meson-gem");
|
||||
if (!drmdrv->gem_client) {
|
||||
DRM_ERROR("open ion client error\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
DRM_DEBUG("open ion client: %p\n", drmdrv->gem_client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void am_meson_gem_cleanup(struct meson_drm *drmdrv)
|
||||
{
|
||||
struct ion_client *gem_ion_client = drmdrv->gem_client;
|
||||
|
||||
if (gem_ion_client) {
|
||||
DRM_DEBUG(" destroy ion client: %p\n", gem_ion_client);
|
||||
ion_client_destroy(gem_ion_client);
|
||||
}
|
||||
}
|
||||
|
||||
/* PRIME operations */
|
||||
struct dma_buf *am_meson_gem_prime_export(
|
||||
struct drm_device *drm_dev,
|
||||
struct drm_gem_object *obj,
|
||||
int flags)
|
||||
{
|
||||
struct am_meson_gem_object *meson_gem_obj;
|
||||
struct ion_handle *handle;
|
||||
|
||||
meson_gem_obj = to_am_meson_gem_obj(obj);
|
||||
handle = meson_gem_obj->handle;
|
||||
|
||||
if (!handle)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return ion_share_dma_buf(handle->client, handle);
|
||||
}
|
||||
|
||||
struct drm_gem_object *am_meson_gem_prime_import(
|
||||
struct drm_device *drm_dev,
|
||||
struct dma_buf *dma_buf)
|
||||
{
|
||||
struct dma_buf_attachment *attach;
|
||||
struct sg_table *sgt;
|
||||
struct am_meson_gem_object *meson_gem_obj = NULL;
|
||||
int ret;
|
||||
|
||||
attach = dma_buf_attach(dma_buf, drm_dev->dev);
|
||||
if (IS_ERR(attach))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
get_dma_buf(dma_buf);
|
||||
|
||||
sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
|
||||
if (IS_ERR_OR_NULL(sgt)) {
|
||||
ret = PTR_ERR(sgt);
|
||||
goto err_buf_detach;
|
||||
}
|
||||
|
||||
meson_gem_obj = kzalloc(sizeof(*meson_gem_obj), GFP_KERNEL);
|
||||
if (!meson_gem_obj) {
|
||||
ret = -ENOMEM;
|
||||
goto err_unmap_attach;
|
||||
}
|
||||
|
||||
ret = drm_gem_object_init(drm_dev, &meson_gem_obj->base, dma_buf->size);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("failed to initialize gem object\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_unmap_attach;
|
||||
}
|
||||
|
||||
meson_gem_obj->sgt = sgt;
|
||||
meson_gem_obj->base.import_attach = attach;
|
||||
|
||||
return &meson_gem_obj->base;
|
||||
|
||||
err_unmap_attach:
|
||||
dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL);
|
||||
err_buf_detach:
|
||||
dma_buf_detach(dma_buf, attach);
|
||||
dma_buf_put(dma_buf);
|
||||
kfree(meson_gem_obj);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
#if 0
|
||||
struct sg_table *am_meson_gem_prime_get_sg_table(struct drm_gem_object *obj)
|
||||
{
|
||||
struct am_meson_gem_object *meson_gem_obj;
|
||||
struct sg_table *src_sgt;
|
||||
struct sg_table *sgt;
|
||||
int ret;
|
||||
|
||||
meson_gem_obj = to_am_meson_gem_obj(obj);
|
||||
DRM_DEBUG("am_meson_gem_prime_get_sg_table %p.\n", meson_gem_obj);
|
||||
|
||||
if (meson_gem_obj->sgt)
|
||||
src_sgt = meson_gem_obj->sgt;
|
||||
else
|
||||
src_sgt = meson_gem_obj->handle->buffer->sg_table;
|
||||
|
||||
sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
|
||||
if (!sgt)
|
||||
return NULL;
|
||||
|
||||
ret = dma_get_sgtable(obj->dev->dev, sgt,
|
||||
page_address(sg_page(src_sgt->sgl)),
|
||||
sg_dma_address(src_sgt->sgl), obj->size);
|
||||
if (ret < 0) {
|
||||
kfree(sgt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sgt;
|
||||
}
|
||||
|
||||
struct drm_gem_object *am_meson_gem_prime_import_sg_table(
|
||||
struct drm_device *dev,
|
||||
struct dma_buf_attachment *attach,
|
||||
struct sg_table *sgt)
|
||||
{
|
||||
struct am_meson_gem_object *meson_gem_obj;
|
||||
int ret;
|
||||
|
||||
meson_gem_obj = kzalloc(sizeof(*meson_gem_obj), GFP_KERNEL);
|
||||
if (!meson_gem_obj)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ret = drm_gem_object_init(dev,
|
||||
&meson_gem_obj->base,
|
||||
attach->dmabuf->size);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("failed to initialize gem object\n");
|
||||
kfree(meson_gem_obj);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
DRM_DEBUG("%s: %p, sg_table %p\n", __func__, meson_gem_obj, sgt);
|
||||
meson_gem_obj->sgt = sgt;
|
||||
return &meson_gem_obj->base;
|
||||
}
|
||||
|
||||
void *am_meson_gem_prime_vmap(struct drm_gem_object *obj)
|
||||
{
|
||||
struct am_meson_gem_object *meson_gem_obj;
|
||||
|
||||
meson_gem_obj = to_am_meson_gem_obj(obj);
|
||||
DRM_DEBUG("am_meson_gem_prime_vmap %p.\n", meson_gem_obj);
|
||||
|
||||
return meson_gem_obj->handle->buffer->vaddr;
|
||||
}
|
||||
|
||||
void am_meson_gem_prime_vunmap(
|
||||
struct drm_gem_object *obj,
|
||||
void *vaddr)
|
||||
{
|
||||
DRM_DEBUG("am_meson_gem_prime_vunmap nothing to do.\n");
|
||||
}
|
||||
|
||||
int am_meson_gem_prime_mmap(
|
||||
struct drm_gem_object *obj,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
struct am_meson_gem_object *meson_gem_obj;
|
||||
int ret;
|
||||
|
||||
ret = drm_gem_mmap_obj(obj, obj->size, vma);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
meson_gem_obj = to_am_meson_gem_obj(obj);
|
||||
DRM_DEBUG("am_meson_gem_prime_mmap %p.\n", meson_gem_obj);
|
||||
|
||||
return am_meson_gem_object_mmap(meson_gem_obj, vma);
|
||||
}
|
||||
#endif
|
||||
79
drivers/amlogic/drm/am_meson_gem.h
Normal file
79
drivers/amlogic/drm/am_meson_gem.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* drivers/amlogic/drm/am_meson_gem.h
|
||||
*
|
||||
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __AM_MESON_GEM_H
|
||||
#define __AM_MESON_GEM_H
|
||||
#include <drm/drm_gem.h>
|
||||
#include <ion/ion_priv.h>
|
||||
#include "meson_drv.h"
|
||||
|
||||
struct am_meson_gem_object {
|
||||
struct drm_gem_object base;
|
||||
unsigned int flags;
|
||||
|
||||
/*for buffer create from ion heap */
|
||||
struct ion_handle *handle;
|
||||
bool bscatter;
|
||||
/*for buffer imported. */
|
||||
struct sg_table *sgt;
|
||||
|
||||
};
|
||||
|
||||
/* GEM MANAGER CREATE*/
|
||||
int am_meson_gem_create(struct meson_drm *drmdrv);
|
||||
|
||||
void am_meson_gem_cleanup(struct meson_drm *drmdrv);
|
||||
|
||||
int am_meson_gem_mmap(
|
||||
struct file *filp,
|
||||
struct vm_area_struct *vma);
|
||||
|
||||
/* GEM DUMB OPERATIONS */
|
||||
int am_meson_gem_dumb_create(
|
||||
struct drm_file *file_priv,
|
||||
struct drm_device *dev,
|
||||
struct drm_mode_create_dumb *args);
|
||||
|
||||
int am_meson_gem_dumb_destroy(
|
||||
struct drm_file *file,
|
||||
struct drm_device *dev,
|
||||
uint32_t handle);
|
||||
|
||||
int am_meson_gem_dumb_map_offset(
|
||||
struct drm_file *file_priv,
|
||||
struct drm_device *dev,
|
||||
uint32_t handle,
|
||||
uint64_t *offset);
|
||||
|
||||
/* GEM OBJECT OPERATIONS */
|
||||
void am_meson_gem_object_free(struct drm_gem_object *gem_obj);
|
||||
|
||||
int am_meson_gem_object_get_phyaddr(
|
||||
struct meson_drm *drm,
|
||||
struct am_meson_gem_object *meson_gem);
|
||||
|
||||
/* GEM PRIME OPERATIONS */
|
||||
struct drm_gem_object *am_meson_gem_prime_import(
|
||||
struct drm_device *drm_dev,
|
||||
struct dma_buf *dma_buf);
|
||||
|
||||
struct dma_buf *am_meson_gem_prime_export(
|
||||
struct drm_device *drm_dev,
|
||||
struct drm_gem_object *obj,
|
||||
int flags);
|
||||
|
||||
#endif /* __AM_MESON_GEM_H */
|
||||
165
drivers/amlogic/drm/am_meson_hdmi.c
Normal file
165
drivers/amlogic/drm/am_meson_hdmi.c
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* drivers/amlogic/drm/am_meson_hdmi.c
|
||||
*
|
||||
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
#include <drm/drm_modeset_helper.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
|
||||
#include "am_meson_hdmi.h"
|
||||
|
||||
struct drm_display_mode am_hdmi_modes[] = {
|
||||
/* 1280x720@60Hz */
|
||||
{ DRM_MODE("720p60hz", DRM_MODE_TYPE_DRIVER |
|
||||
DRM_MODE_TYPE_PREFERRED, 74250, 1280, 1390,
|
||||
1430, 1650, 0, 720, 725, 730, 750, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
|
||||
.vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,},
|
||||
|
||||
/* 1280x720@50Hz */
|
||||
{ DRM_MODE("720p50hz", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720,
|
||||
1760, 1980, 0, 720, 725, 730, 750, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
|
||||
.vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
|
||||
|
||||
/* 1920x1080@60Hz */
|
||||
{ DRM_MODE("1080p60hz", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
|
||||
2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
|
||||
.vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
|
||||
|
||||
/* 1920x1080@50Hz */
|
||||
{ DRM_MODE("1080p50hz", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
|
||||
2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
|
||||
.vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
|
||||
|
||||
/* 3840x2160@30Hz */
|
||||
{ DRM_MODE("2160p30hz", DRM_MODE_TYPE_DRIVER, 297000,
|
||||
3840, 4016, 4104, 4400, 0,
|
||||
2160, 2168, 2178, 2250, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
|
||||
.vrefresh = 30, },
|
||||
};
|
||||
|
||||
int am_hdmi_tx_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
int count;
|
||||
struct drm_display_mode *mode;
|
||||
|
||||
for (count = 0; count < ARRAY_SIZE(am_hdmi_modes); count++) {
|
||||
mode = drm_mode_duplicate(connector->dev,
|
||||
&am_hdmi_modes[count]);
|
||||
drm_mode_probed_add(connector, mode);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
enum drm_mode_status am_hdmi_tx_check_mode(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
|
||||
static const
|
||||
struct drm_connector_helper_funcs am_hdmi_connector_helper_funcs = {
|
||||
.get_modes = am_hdmi_tx_get_modes,
|
||||
.mode_valid = am_hdmi_tx_check_mode,
|
||||
};
|
||||
|
||||
static enum drm_connector_status
|
||||
am_hdmi_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
/* FIXME: Add load-detect or jack-detect if possible */
|
||||
return connector_status_connected;
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs am_hdmi_connector_funcs = {
|
||||
.dpms = drm_atomic_helper_connector_dpms,
|
||||
.detect = am_hdmi_connector_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = drm_connector_cleanup,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static void am_hdmi_encoder_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
//DRM_DEBUG("am_hdmi_encoder_mode_set ");
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs
|
||||
am_hdmi_encoder_helper_funcs = {
|
||||
.mode_set = am_hdmi_encoder_mode_set,
|
||||
};
|
||||
|
||||
static const struct drm_encoder_funcs am_hdmi_encoder_funcs = {
|
||||
.destroy = drm_encoder_cleanup,
|
||||
};
|
||||
|
||||
|
||||
int am_hdmi_connector_create(struct meson_drm *priv)
|
||||
{
|
||||
struct drm_device *drm = priv->drm;
|
||||
struct am_hdmi_tx *am_hdmi;
|
||||
struct drm_connector *connector;
|
||||
struct drm_encoder *encoder;
|
||||
int ret;
|
||||
|
||||
am_hdmi = devm_kzalloc(priv->dev, sizeof(*am_hdmi),
|
||||
GFP_KERNEL);
|
||||
if (!am_hdmi)
|
||||
return -ENOMEM;
|
||||
|
||||
am_hdmi->priv = priv;
|
||||
|
||||
encoder = &am_hdmi->encoder;
|
||||
connector = &am_hdmi->connector;
|
||||
|
||||
/* Connector */
|
||||
drm_connector_helper_add(connector,
|
||||
&am_hdmi_connector_helper_funcs);
|
||||
|
||||
ret = drm_connector_init(drm, connector, &am_hdmi_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_HDMIA);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "Failed to init hdmi tx connector\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
connector->interlace_allowed = 1;
|
||||
|
||||
/* Encoder */
|
||||
drm_encoder_helper_add(encoder, &am_hdmi_encoder_helper_funcs);
|
||||
|
||||
ret = drm_encoder_init(drm, encoder, &am_hdmi_encoder_funcs,
|
||||
DRM_MODE_ENCODER_TVDAC, "am_hdmi_encoder");
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "Failed to init hdmi encoder\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
encoder->possible_crtcs = BIT(0);
|
||||
|
||||
drm_mode_connector_attach_encoder(connector, encoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* drivers/amlogic/drm/meson_dmabuf.h
|
||||
* drivers/amlogic/drm/am_meson_hdmi.h
|
||||
*
|
||||
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
@@ -14,14 +14,18 @@
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
#ifndef __AM_MESON_HDMI_H
|
||||
#define __AM_MESON_HDMI_H
|
||||
|
||||
#ifndef _MESON_DRM_DMABUF_H_
|
||||
#define _MESON_DRM_DMABUF_H_
|
||||
#include "meson_drv.h"
|
||||
|
||||
struct dma_buf *meson_dmabuf_prime_export(struct drm_device *drm_dev,
|
||||
struct drm_gem_object *obj, int flags);
|
||||
struct am_hdmi_tx {
|
||||
struct drm_encoder encoder;
|
||||
struct drm_connector connector;
|
||||
struct meson_drm *priv;
|
||||
};
|
||||
|
||||
struct drm_gem_object *meson_dmabuf_prime_import(struct drm_device *drm_dev,
|
||||
struct dma_buf *dma_buf);
|
||||
int am_hdmi_connector_create(struct meson_drm *priv);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* MESON_DRM_DMABUF_H */
|
||||
258
drivers/amlogic/drm/am_meson_plane.c
Normal file
258
drivers/amlogic/drm/am_meson_plane.c
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* drivers/amlogic/drm/am_meson_plane.c
|
||||
*
|
||||
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_plane.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
|
||||
#include "osd.h"
|
||||
#include "osd_drm.h"
|
||||
|
||||
#include "meson_plane.h"
|
||||
#ifdef CONFIG_DRM_MESON_USE_ION
|
||||
#include "am_meson_fb.h"
|
||||
#endif
|
||||
|
||||
#define to_am_osd_plane(x) container_of(x, struct am_osd_plane, base)
|
||||
|
||||
struct am_osd_plane {
|
||||
struct drm_plane base; //must be first element.
|
||||
struct meson_drm *drv; //point to struct parent.
|
||||
struct dentry *plane_debugfs_dir;
|
||||
|
||||
u32 osd_idx;
|
||||
};
|
||||
|
||||
static const struct drm_plane_funcs am_osd_plane_funs = {
|
||||
.update_plane = drm_atomic_helper_update_plane,
|
||||
.disable_plane = drm_atomic_helper_disable_plane,
|
||||
.destroy = drm_plane_cleanup,
|
||||
.reset = drm_atomic_helper_plane_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
|
||||
};
|
||||
|
||||
int am_osd_begin_display(
|
||||
struct drm_plane *plane,
|
||||
struct drm_plane_state *new_state)
|
||||
{
|
||||
struct am_osd_plane *osd_plane = to_am_osd_plane(plane);
|
||||
|
||||
DRM_DEBUG("%s osd %d.\n", __func__, osd_plane->osd_idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void am_osd_end_display(
|
||||
struct drm_plane *plane,
|
||||
struct drm_plane_state *old_state)
|
||||
{
|
||||
struct am_osd_plane *osd_plane = to_am_osd_plane(plane);
|
||||
|
||||
DRM_DEBUG("%s osd %d.\n", __func__, osd_plane->osd_idx);
|
||||
}
|
||||
|
||||
void am_osd_do_display(
|
||||
struct drm_plane *plane,
|
||||
struct drm_plane_state *old_state)
|
||||
{
|
||||
struct am_osd_plane *osd_plane = to_am_osd_plane(plane);
|
||||
struct drm_plane_state *state = plane->state;
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
struct meson_drm *drv = osd_plane->drv;
|
||||
struct osd_plane_map_s plane_map;
|
||||
#ifdef CONFIG_DRM_MESON_USE_ION
|
||||
struct am_meson_fb *meson_fb;
|
||||
#else
|
||||
struct drm_gem_cma_object *gem;
|
||||
#endif
|
||||
int format = DRM_FORMAT_ARGB8888;
|
||||
dma_addr_t phyaddr;
|
||||
unsigned long flags;
|
||||
|
||||
//DRM_INFO("am_osd_do_display osd %d.\n", osd_plane->osd_idx);
|
||||
|
||||
switch (fb->pixel_format) {
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
format = COLOR_INDEX_32_XRGB;
|
||||
break;
|
||||
case DRM_FORMAT_XBGR8888:
|
||||
format = COLOR_INDEX_32_XBGR;
|
||||
break;
|
||||
case DRM_FORMAT_RGBX8888:
|
||||
format = COLOR_INDEX_32_RGBX;
|
||||
break;
|
||||
case DRM_FORMAT_BGRX8888:
|
||||
format = COLOR_INDEX_32_BGRX;
|
||||
break;
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
format = COLOR_INDEX_32_ARGB;
|
||||
break;
|
||||
case DRM_FORMAT_ABGR8888:
|
||||
format = COLOR_INDEX_32_ABGR;
|
||||
break;
|
||||
case DRM_FORMAT_RGBA8888:
|
||||
format = COLOR_INDEX_32_RGBA;
|
||||
break;
|
||||
case DRM_FORMAT_BGRA8888:
|
||||
format = COLOR_INDEX_32_BGRA;
|
||||
break;
|
||||
case DRM_FORMAT_RGB888:
|
||||
format = COLOR_INDEX_24_RGB;
|
||||
break;
|
||||
case DRM_FORMAT_RGB565:
|
||||
format = COLOR_INDEX_16_565;
|
||||
break;
|
||||
case DRM_FORMAT_ARGB1555:
|
||||
format = COLOR_INDEX_16_1555_A;
|
||||
break;
|
||||
case DRM_FORMAT_ARGB4444:
|
||||
format = COLOR_INDEX_16_4444_A;
|
||||
break;
|
||||
default:
|
||||
DRM_INFO("unsupport fb->pixel_format=%x\n", fb->pixel_format);
|
||||
break;
|
||||
};
|
||||
|
||||
spin_lock_irqsave(&drv->drm->event_lock, flags);
|
||||
|
||||
#ifdef CONFIG_DRM_MESON_USE_ION
|
||||
meson_fb = container_of(fb, struct am_meson_fb, base);
|
||||
phyaddr = am_meson_gem_object_get_phyaddr(drv, meson_fb->bufp);
|
||||
if (meson_fb->bufp->bscatter)
|
||||
DRM_ERROR("ERROR:am_meson_plane meet a scatter framebuffe.\nr");
|
||||
#else
|
||||
/* Update Canvas with buffer address */
|
||||
gem = drm_fb_cma_get_gem_obj(fb, 0);
|
||||
phyaddr = gem->paddr;
|
||||
#endif
|
||||
|
||||
/* setup osd display parameters */
|
||||
plane_map.plane_index = osd_plane->osd_idx;
|
||||
plane_map.zorder = state->zpos;
|
||||
plane_map.phy_addr = phyaddr;
|
||||
plane_map.enable = 1;
|
||||
plane_map.format = format;
|
||||
plane_map.byte_stride = fb->pitches[0];
|
||||
|
||||
plane_map.src_x = state->src_x;
|
||||
plane_map.src_y = state->src_y;
|
||||
plane_map.src_w = (state->src_w >> 16) & 0xffff;
|
||||
plane_map.src_h = (state->src_h >> 16) & 0xffff;
|
||||
|
||||
plane_map.dst_x = state->crtc_x;
|
||||
plane_map.dst_y = state->crtc_y;
|
||||
plane_map.dst_w = state->crtc_w;
|
||||
plane_map.dst_h = state->crtc_h;
|
||||
#if 0
|
||||
DRM_INFO("flags:%d pixel_format:%d,zpos=%d\n",
|
||||
fb->flags, fb->pixel_format, state->zpos);
|
||||
DRM_INFO("plane index=%d, type=%d\n", plane->index, plane->type);
|
||||
#endif
|
||||
osd_drm_plane_page_flip(&plane_map);
|
||||
|
||||
spin_unlock_irqrestore(&drv->drm->event_lock, flags);
|
||||
}
|
||||
|
||||
int am_osd_check(struct drm_plane *plane, struct drm_plane_state *state)
|
||||
{
|
||||
struct am_osd_plane *osd_plane = to_am_osd_plane(plane);
|
||||
|
||||
DRM_DEBUG("am_osd_check osd %d.\n", osd_plane->osd_idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void am_osd_blank(struct drm_plane *plane, struct drm_plane_state *old_state)
|
||||
{
|
||||
struct am_osd_plane *osd_plane = to_am_osd_plane(plane);
|
||||
|
||||
DRM_DEBUG("am_osd_blank osd %d.\n", osd_plane->osd_idx);
|
||||
}
|
||||
|
||||
static const struct drm_plane_helper_funcs am_osd_helper_funcs = {
|
||||
.prepare_fb = am_osd_begin_display,
|
||||
.cleanup_fb = am_osd_end_display,
|
||||
.atomic_update = am_osd_do_display,
|
||||
.atomic_check = am_osd_check,
|
||||
.atomic_disable = am_osd_blank,
|
||||
};
|
||||
|
||||
static const uint32_t supported_drm_formats[] = {
|
||||
DRM_FORMAT_ARGB8888,
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_RGB888,
|
||||
DRM_FORMAT_RGB565,
|
||||
};
|
||||
|
||||
struct am_osd_plane *am_osd_plane_create(struct meson_drm *priv, u32 type)
|
||||
{
|
||||
struct am_osd_plane *osd_plane;
|
||||
struct drm_plane *plane;
|
||||
char *plane_name = NULL;
|
||||
|
||||
osd_plane = devm_kzalloc(priv->drm->dev, sizeof(*osd_plane),
|
||||
GFP_KERNEL);
|
||||
if (!osd_plane)
|
||||
return 0;
|
||||
|
||||
osd_plane->drv = priv;
|
||||
plane = &osd_plane->base;
|
||||
|
||||
if (type == DRM_PLANE_TYPE_PRIMARY) {
|
||||
osd_plane->osd_idx = 0;
|
||||
plane_name = "osd-0";
|
||||
} else if (type == DRM_PLANE_TYPE_CURSOR) {
|
||||
osd_plane->osd_idx = 1;
|
||||
plane_name = "osd-1";
|
||||
}
|
||||
|
||||
drm_universal_plane_init(priv->drm, plane, 0xFF,
|
||||
&am_osd_plane_funs,
|
||||
supported_drm_formats,
|
||||
ARRAY_SIZE(supported_drm_formats),
|
||||
type, plane_name);
|
||||
|
||||
drm_plane_helper_add(plane, &am_osd_helper_funcs);
|
||||
osd_drm_debugfs_add(&(osd_plane->plane_debugfs_dir),
|
||||
plane_name, osd_plane->osd_idx);
|
||||
return osd_plane;
|
||||
}
|
||||
|
||||
int meson_plane_create(struct meson_drm *priv)
|
||||
{
|
||||
struct am_osd_plane *plane;
|
||||
|
||||
DRM_DEBUG("amlogic meson_plane_create. enter\n");
|
||||
/*crate primary plane*/
|
||||
plane = am_osd_plane_create(priv, DRM_PLANE_TYPE_PRIMARY);
|
||||
if (plane == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->primary_plane = &(plane->base);
|
||||
|
||||
/*crate cursor plane*/
|
||||
plane = am_osd_plane_create(priv, DRM_PLANE_TYPE_CURSOR);
|
||||
if (plane == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->cursor_plane = &(plane->base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* drivers/amlogic/drm/meson_dmabuf.c
|
||||
*
|
||||
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <ion/ion.h>
|
||||
|
||||
#include "meson_gem.h"
|
||||
#include "meson_dmabuf.h"
|
||||
|
||||
struct dma_buf *meson_dmabuf_prime_export(struct drm_device *drm_dev,
|
||||
struct drm_gem_object *obj, int flags)
|
||||
{
|
||||
struct meson_gem_object *meson_gem_obj;
|
||||
struct ion_handle *handle;
|
||||
|
||||
meson_gem_obj = container_of(obj, struct meson_gem_object, base);
|
||||
handle = meson_gem_obj->handle;
|
||||
|
||||
if (!handle)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return ion_share_dma_buf(handle->client, handle);
|
||||
}
|
||||
|
||||
struct drm_gem_object *meson_dmabuf_prime_import(struct drm_device *drm_dev,
|
||||
struct dma_buf *dma_buf)
|
||||
{
|
||||
struct dma_buf_attachment *attach;
|
||||
struct sg_table *sgt;
|
||||
struct meson_gem_object *meson_gem_obj;
|
||||
int ret;
|
||||
|
||||
attach = dma_buf_attach(dma_buf, drm_dev->dev);
|
||||
if (IS_ERR(attach))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
get_dma_buf(dma_buf);
|
||||
|
||||
sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
|
||||
if (IS_ERR_OR_NULL(sgt)) {
|
||||
ret = PTR_ERR(sgt);
|
||||
goto err_buf_detach;
|
||||
}
|
||||
|
||||
meson_gem_obj = meson_drm_gem_init(drm_dev, dma_buf->size);
|
||||
if (!meson_gem_obj) {
|
||||
ret = -ENOMEM;
|
||||
goto err_unmap_attach;
|
||||
}
|
||||
|
||||
meson_gem_obj->sgt = sgt;
|
||||
meson_gem_obj->base.import_attach = attach;
|
||||
|
||||
return &meson_gem_obj->base;
|
||||
|
||||
err_unmap_attach:
|
||||
dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL);
|
||||
err_buf_detach:
|
||||
dma_buf_detach(dma_buf, attach);
|
||||
dma_buf_put(dma_buf);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
@@ -27,16 +27,11 @@
|
||||
#include <linux/of_graph.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
|
||||
/* #define MUSE */
|
||||
#ifdef MUSE
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_flip_work.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#endif
|
||||
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_rect.h>
|
||||
@@ -46,14 +41,20 @@
|
||||
#include "meson_plane.h"
|
||||
#include "meson_crtc.h"
|
||||
#include "meson_venc_cvbs.h"
|
||||
|
||||
#ifdef CONFIG_DRM_MESON_BYPASS_MODE
|
||||
#include "am_meson_hdmi.h"
|
||||
#include "osd_drm.h"
|
||||
#else
|
||||
#include "meson_vpp.h"
|
||||
#include "meson_viu.h"
|
||||
#include "meson_venc.h"
|
||||
#include "meson_canvas.h"
|
||||
#include "meson_registers.h"
|
||||
#include "meson_gem.h"
|
||||
#include "meson_dmabuf.h"
|
||||
#endif
|
||||
#ifdef CONFIG_DRM_MESON_USE_ION
|
||||
#include "am_meson_gem.h"
|
||||
#include "am_meson_fb.h"
|
||||
#endif
|
||||
|
||||
#define DRIVER_NAME "meson"
|
||||
#define DRIVER_DESC "Amlogic Meson DRM driver"
|
||||
@@ -71,35 +72,42 @@
|
||||
* - Powering up video processing HW blocks
|
||||
* - Powering Up HDMI controller and PHY
|
||||
*/
|
||||
|
||||
#ifdef MUSE
|
||||
static void meson_fb_output_poll_changed(struct drm_device *dev)
|
||||
{
|
||||
#ifdef CONFIG_DRM_MESON_EMULATE_FBDEV
|
||||
struct meson_drm *priv = dev->dev_private;
|
||||
|
||||
drm_fbdev_cma_hotplug_event(priv->fbdev);
|
||||
#endif
|
||||
}
|
||||
|
||||
static const struct drm_mode_config_funcs meson_mode_config_funcs = {
|
||||
.output_poll_changed = meson_fb_output_poll_changed,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
#ifdef CONFIG_DRM_MESON_USE_ION
|
||||
.fb_create = am_meson_fb_create,
|
||||
#else
|
||||
.fb_create = drm_fb_cma_create,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int meson_enable_vblank(struct drm_device *dev, unsigned int crtc)
|
||||
{
|
||||
#ifndef CONFIG_DRM_MESON_BYPASS_MODE
|
||||
struct meson_drm *priv = dev->dev_private;
|
||||
|
||||
meson_venc_enable_vsync(priv);
|
||||
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void meson_disable_vblank(struct drm_device *dev, unsigned int crtc)
|
||||
{
|
||||
#ifndef CONFIG_DRM_MESON_BYPASS_MODE
|
||||
struct meson_drm *priv = dev->dev_private;
|
||||
|
||||
meson_venc_disable_vsync(priv);
|
||||
#endif
|
||||
}
|
||||
|
||||
static irqreturn_t meson_irq(int irq, void *arg)
|
||||
@@ -107,13 +115,14 @@ static irqreturn_t meson_irq(int irq, void *arg)
|
||||
struct drm_device *dev = arg;
|
||||
struct meson_drm *priv = dev->dev_private;
|
||||
|
||||
#ifndef CONFIG_DRM_MESON_BYPASS_MODE
|
||||
(void)readl_relaxed(priv->io_base + _REG(VENC_INTFLAG));
|
||||
#endif
|
||||
|
||||
meson_crtc_irq(priv);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct file_operations fops = {
|
||||
.owner = THIS_MODULE,
|
||||
@@ -126,16 +135,18 @@ static const struct file_operations fops = {
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.llseek = no_llseek,
|
||||
#ifdef CONFIG_DRM_MESON_USE_ION
|
||||
.mmap = am_meson_gem_mmap,
|
||||
#else
|
||||
.mmap = drm_gem_cma_mmap,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct drm_driver meson_driver = {
|
||||
.driver_features = DRIVER_GEM | DRIVER_PRIME,
|
||||
|
||||
#ifdef MUSE
|
||||
.driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM |
|
||||
DRIVER_MODESET | DRIVER_PRIME |
|
||||
DRIVER_ATOMIC,
|
||||
DRIVER_ATOMIC | DRIVER_IRQ_SHARED,
|
||||
|
||||
/* Vblank */
|
||||
.enable_vblank = meson_enable_vblank,
|
||||
@@ -144,22 +155,40 @@ static struct drm_driver meson_driver = {
|
||||
|
||||
/* IRQ */
|
||||
.irq_handler = meson_irq,
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_MESON_USE_ION
|
||||
/* PRIME Ops */
|
||||
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
|
||||
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
|
||||
.gem_prime_import = meson_dmabuf_prime_import,
|
||||
.gem_prime_export = meson_dmabuf_prime_export,
|
||||
.gem_prime_import = am_meson_gem_prime_import,
|
||||
.gem_prime_export = am_meson_gem_prime_export,
|
||||
|
||||
/* GEM Ops */
|
||||
.open = meson_drm_gem_open,
|
||||
.postclose = meson_drm_gem_close,
|
||||
.dumb_create = meson_drm_gem_dumb_create,
|
||||
.dumb_destroy = meson_drm_gem_dumb_destroy,
|
||||
.dumb_map_offset = meson_drm_gem_dumb_map_offset,
|
||||
.gem_free_object = meson_drm_gem_free_object,
|
||||
.dumb_create = am_meson_gem_dumb_create,
|
||||
.dumb_destroy = am_meson_gem_dumb_destroy,
|
||||
.dumb_map_offset = am_meson_gem_dumb_map_offset,
|
||||
|
||||
.gem_free_object_unlocked = am_meson_gem_object_free,
|
||||
.gem_vm_ops = &drm_gem_cma_vm_ops,
|
||||
#else
|
||||
/* PRIME Ops */
|
||||
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
|
||||
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
|
||||
.gem_prime_import = drm_gem_prime_import,
|
||||
.gem_prime_export = drm_gem_prime_export,
|
||||
.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
|
||||
.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
|
||||
.gem_prime_vmap = drm_gem_cma_prime_vmap,
|
||||
.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
|
||||
.gem_prime_mmap = drm_gem_cma_prime_mmap,
|
||||
|
||||
/* GEM Ops */
|
||||
.dumb_create = drm_gem_cma_dumb_create,
|
||||
.dumb_destroy = drm_gem_dumb_destroy,
|
||||
.dumb_map_offset = drm_gem_cma_dumb_map_offset,
|
||||
.gem_free_object_unlocked = drm_gem_cma_free_object,
|
||||
.gem_vm_ops = &drm_gem_cma_vm_ops,
|
||||
#endif
|
||||
|
||||
/* Misc */
|
||||
.fops = &fops,
|
||||
@@ -170,7 +199,7 @@ static struct drm_driver meson_driver = {
|
||||
.minor = 0,
|
||||
};
|
||||
|
||||
#ifdef MUSE
|
||||
#ifndef CONFIG_DRM_MESON_BYPASS_MODE
|
||||
static bool meson_vpu_has_available_connectors(struct device *dev)
|
||||
{
|
||||
struct device_node *ep, *remote;
|
||||
@@ -201,7 +230,7 @@ static int meson_drv_probe(struct platform_device *pdev)
|
||||
struct drm_device *drm;
|
||||
int ret;
|
||||
|
||||
#ifdef MUSE
|
||||
#ifndef CONFIG_DRM_MESON_BYPASS_MODE
|
||||
struct resource *res;
|
||||
void __iomem *regs;
|
||||
|
||||
@@ -225,7 +254,7 @@ static int meson_drv_probe(struct platform_device *pdev)
|
||||
priv->drm = drm;
|
||||
priv->dev = dev;
|
||||
|
||||
#ifdef MUSE
|
||||
#ifndef CONFIG_DRM_MESON_BYPASS_MODE
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpu");
|
||||
regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(regs))
|
||||
@@ -258,14 +287,20 @@ static int meson_drv_probe(struct platform_device *pdev)
|
||||
dev_err(&pdev->dev, "Couldn't create the DMC regmap\n");
|
||||
return PTR_ERR(priv->dmc);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_MESON_USE_ION
|
||||
am_meson_gem_create(priv);
|
||||
#endif
|
||||
|
||||
priv->vsync_irq = platform_get_irq(pdev, 0);
|
||||
|
||||
drm_vblank_init(drm, 1);
|
||||
drm_mode_config_init(drm);
|
||||
|
||||
#ifdef CONFIG_DRM_MESON_BYPASS_MODE
|
||||
am_hdmi_connector_create(priv);
|
||||
#else
|
||||
/* Encoder Initialization */
|
||||
|
||||
ret = meson_venc_cvbs_create(priv);
|
||||
if (ret)
|
||||
goto free_drm;
|
||||
@@ -275,6 +310,7 @@ static int meson_drv_probe(struct platform_device *pdev)
|
||||
meson_venc_init(priv);
|
||||
meson_vpp_init(priv);
|
||||
meson_viu_init(priv);
|
||||
#endif
|
||||
|
||||
ret = meson_plane_create(priv);
|
||||
if (ret)
|
||||
@@ -293,6 +329,7 @@ static int meson_drv_probe(struct platform_device *pdev)
|
||||
drm->mode_config.max_height = 8192;
|
||||
drm->mode_config.funcs = &meson_mode_config_funcs;
|
||||
|
||||
#ifdef CONFIG_DRM_MESON_EMULATE_FBDEV
|
||||
priv->fbdev = drm_fbdev_cma_init(drm, 32,
|
||||
drm->mode_config.num_crtc,
|
||||
drm->mode_config.num_connector);
|
||||
@@ -300,9 +337,9 @@ static int meson_drv_probe(struct platform_device *pdev)
|
||||
ret = PTR_ERR(priv->fbdev);
|
||||
goto free_drm;
|
||||
}
|
||||
#endif
|
||||
|
||||
drm_kms_helper_poll_init(drm);
|
||||
#endif
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
@@ -310,14 +347,15 @@ static int meson_drv_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto free_drm;
|
||||
|
||||
#ifdef CONFIG_DRM_MESON_BYPASS_MODE
|
||||
osd_drm_debugfs_init();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
free_drm:
|
||||
DRM_DEBUG("free-drm");
|
||||
|
||||
#ifdef MUSE
|
||||
drm_dev_unref(drm);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -325,18 +363,22 @@ free_drm:
|
||||
static int meson_drv_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct drm_device *drm = dev_get_drvdata(&pdev->dev);
|
||||
#ifdef MUSE
|
||||
struct meson_drm *priv = drm->dev_private;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_MESON_BYPASS_MODE
|
||||
osd_drm_debugfs_exit();
|
||||
#endif
|
||||
drm_dev_unregister(drm);
|
||||
#ifdef MUSE
|
||||
drm_kms_helper_poll_fini(drm);
|
||||
#ifdef CONFIG_DRM_MESON_EMULATE_FBDEV
|
||||
drm_fbdev_cma_fini(priv->fbdev);
|
||||
#endif
|
||||
drm_mode_config_cleanup(drm);
|
||||
drm_vblank_cleanup(drm);
|
||||
drm_dev_unref(drm);
|
||||
#ifdef CONFIG_DRM_MESON_USE_ION
|
||||
am_meson_gem_cleanup(priv);
|
||||
#endif
|
||||
drm_dev_unref(drm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -363,5 +405,6 @@ module_platform_driver(meson_drm_platform_driver);
|
||||
|
||||
MODULE_AUTHOR("Jasper St. Pierre <jstpierre@mecheye.net>");
|
||||
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
|
||||
MODULE_AUTHOR("MultiMedia Amlogic <multimedia-sh@amlogic.com>");
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -23,19 +23,28 @@
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/of.h>
|
||||
#include <drm/drmP.h>
|
||||
#ifdef CONFIG_DRM_MESON_USE_ION
|
||||
#include <ion/ion_priv.h>
|
||||
#endif
|
||||
|
||||
struct meson_drm {
|
||||
struct device *dev;
|
||||
|
||||
#ifndef CONFIG_DRM_MESON_BYPASS_MODE
|
||||
void __iomem *io_base;
|
||||
struct regmap *hhi;
|
||||
struct regmap *dmc;
|
||||
#endif
|
||||
|
||||
int vsync_irq;
|
||||
|
||||
struct drm_device *drm;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_fbdev_cma *fbdev;
|
||||
struct drm_plane *primary_plane;
|
||||
struct drm_plane *cursor_plane;
|
||||
|
||||
#ifndef CONFIG_DRM_MESON_BYPASS_MODE
|
||||
/* Components Data */
|
||||
struct {
|
||||
bool osd1_enabled;
|
||||
@@ -48,6 +57,11 @@ struct meson_drm {
|
||||
struct {
|
||||
unsigned int current_mode;
|
||||
} venc;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_MESON_USE_ION
|
||||
struct ion_client *gem_client;
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline int meson_vpu_is_compatible(struct meson_drm *priv,
|
||||
|
||||
@@ -1,313 +0,0 @@
|
||||
/*
|
||||
* drivers/amlogic/drm/meson_gem.c
|
||||
*
|
||||
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_gem.h>
|
||||
#include <drm/drm_vma_manager.h>
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <ion/ion.h>
|
||||
#include <linux/meson_ion.h>
|
||||
|
||||
#include "meson_drv.h"
|
||||
#include "meson_gem.h"
|
||||
|
||||
|
||||
struct meson_gem_object *meson_drm_gem_init(struct drm_device *dev,
|
||||
unsigned long size)
|
||||
{
|
||||
struct meson_gem_object *meson_gem_obj;
|
||||
struct drm_gem_object *obj;
|
||||
int ret;
|
||||
|
||||
meson_gem_obj = kzalloc(sizeof(*meson_gem_obj), GFP_KERNEL);
|
||||
if (!meson_gem_obj)
|
||||
return NULL;
|
||||
|
||||
meson_gem_obj->size = size;
|
||||
obj = &meson_gem_obj->base;
|
||||
|
||||
ret = drm_gem_object_init(dev, obj, size);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("failed to initialize gem object\n");
|
||||
kfree(meson_gem_obj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DRM_DEBUG("created file object = 0x%lx\n", (unsigned long)obj->filp);
|
||||
|
||||
return meson_gem_obj;
|
||||
}
|
||||
|
||||
static int meson_drm_alloc_buf(struct ion_client *client,
|
||||
struct meson_gem_object *meson_gem_obj, int flags)
|
||||
{
|
||||
if (!client)
|
||||
return -EINVAL;
|
||||
|
||||
if (!meson_gem_obj)
|
||||
return -EINVAL;
|
||||
|
||||
meson_gem_obj->handle = ion_alloc(client,
|
||||
meson_gem_obj->size,
|
||||
0,
|
||||
(1 << ION_HEAP_TYPE_SYSTEM),
|
||||
0);
|
||||
|
||||
if (!meson_gem_obj->handle)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct meson_gem_object *meson_drm_gem_create(struct drm_device *dev,
|
||||
unsigned int flags,
|
||||
unsigned long size,
|
||||
struct ion_client *client)
|
||||
{
|
||||
struct meson_gem_object *meson_gem_obj;
|
||||
int ret;
|
||||
|
||||
if (!size) {
|
||||
DRM_ERROR("invalid size.\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
size = round_up(size, PAGE_SIZE);
|
||||
meson_gem_obj = meson_drm_gem_init(dev, size);
|
||||
if (!meson_gem_obj) {
|
||||
ret = -ENOMEM;
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
ret = meson_drm_alloc_buf(client, meson_gem_obj, flags);
|
||||
if (ret < 0)
|
||||
goto err_gem_fini;
|
||||
|
||||
return meson_gem_obj;
|
||||
|
||||
err_gem_fini:
|
||||
drm_gem_object_release(&meson_gem_obj->base);
|
||||
kfree(meson_gem_obj);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static int meson_drm_gem_handle_create(struct drm_gem_object *obj,
|
||||
struct drm_file *file_priv,
|
||||
unsigned int *handle)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* allocate a id of idr table where the obj is registered
|
||||
* and handle has the id what user can see.
|
||||
*/
|
||||
ret = drm_gem_handle_create(file_priv, obj, handle);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
DRM_DEBUG("gem handle = 0x%x\n", *handle);
|
||||
|
||||
/* drop reference from allocate - handle holds it now. */
|
||||
drm_gem_object_unreference_unlocked(obj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void meson_drm_free_buf(struct drm_device *dev,
|
||||
struct meson_gem_object *meson_gem_obj)
|
||||
{
|
||||
struct ion_client *client = NULL;
|
||||
|
||||
if (meson_gem_obj->handle) {
|
||||
client = meson_gem_obj->handle->client;
|
||||
ion_free(client, meson_gem_obj->handle);
|
||||
} else {
|
||||
DRM_ERROR("meson_gem_obj handle is null\n");
|
||||
}
|
||||
}
|
||||
|
||||
void meson_drm_gem_destroy(struct meson_gem_object *meson_gem_obj)
|
||||
{
|
||||
struct drm_gem_object *obj;
|
||||
|
||||
obj = &meson_gem_obj->base;
|
||||
|
||||
DRM_DEBUG("handle count = %d\n", obj->handle_count);
|
||||
|
||||
/*
|
||||
* do not release memory region from exporter.
|
||||
*
|
||||
* the region will be released by exporter
|
||||
* once dmabuf's refcount becomes 0.
|
||||
*/
|
||||
if (obj->import_attach)
|
||||
goto out;
|
||||
|
||||
meson_drm_free_buf(obj->dev, meson_gem_obj);
|
||||
|
||||
out:
|
||||
|
||||
drm_gem_free_mmap_offset(obj);
|
||||
|
||||
/* release file pointer to gem object. */
|
||||
drm_gem_object_release(obj);
|
||||
|
||||
kfree(meson_gem_obj);
|
||||
meson_gem_obj = NULL;
|
||||
}
|
||||
|
||||
int meson_drm_gem_dumb_create(struct drm_file *file_priv,
|
||||
struct drm_device *dev, struct drm_mode_create_dumb *args)
|
||||
{
|
||||
int ret = 0;
|
||||
struct meson_gem_object *meson_gem_obj;
|
||||
struct drm_meson_file_private *f = file_priv->driver_priv;
|
||||
|
||||
struct ion_client *client = (struct ion_client *) f->client;
|
||||
int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
|
||||
|
||||
args->pitch = ALIGN(min_pitch, 64);
|
||||
if (args->size < args->pitch * args->height)
|
||||
args->size = args->pitch * args->height;
|
||||
|
||||
meson_gem_obj = meson_drm_gem_create(dev, 0, args->size, client);
|
||||
|
||||
if (IS_ERR(meson_gem_obj))
|
||||
return PTR_ERR(meson_gem_obj);
|
||||
|
||||
ret = meson_drm_gem_handle_create(&meson_gem_obj->base, file_priv,
|
||||
&args->handle);
|
||||
if (ret) {
|
||||
meson_drm_gem_destroy(meson_gem_obj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int meson_drm_gem_dumb_map_offset(struct drm_file *file_priv,
|
||||
struct drm_device *dev, uint32_t handle,
|
||||
uint64_t *offset)
|
||||
{
|
||||
struct drm_gem_object *obj;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
|
||||
/*
|
||||
* get offset of memory allocated for drm framebuffer.
|
||||
* - this callback would be called by user application
|
||||
* with DRM_IOCTL_MODE_MAP_DUMB command.
|
||||
*/
|
||||
|
||||
obj = drm_gem_object_lookup(file_priv, handle);
|
||||
if (!obj) {
|
||||
DRM_ERROR("failed to lookup gem object.\n");
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = drm_gem_create_mmap_offset(obj);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
*offset = drm_vma_node_offset_addr(&obj->vma_node);
|
||||
DRM_DEBUG("offset = 0x%lx\n", (unsigned long)*offset);
|
||||
|
||||
out:
|
||||
drm_gem_object_unreference(obj);
|
||||
unlock:
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void meson_drm_gem_free_object(struct drm_gem_object *obj)
|
||||
{
|
||||
struct meson_gem_object *meson_gem_obj;
|
||||
|
||||
meson_gem_obj = container_of(obj, struct meson_gem_object, base);
|
||||
|
||||
if (obj->import_attach)
|
||||
drm_prime_gem_destroy(obj, meson_gem_obj->sgt);
|
||||
|
||||
meson_drm_gem_destroy(meson_gem_obj);
|
||||
}
|
||||
|
||||
int meson_drm_gem_dumb_destroy(struct drm_file *file,
|
||||
struct drm_device *dev,
|
||||
uint32_t handle)
|
||||
{
|
||||
struct drm_gem_object *obj;
|
||||
|
||||
spin_lock(&file->table_lock);
|
||||
obj = idr_find(&file->object_idr, handle);
|
||||
if (obj == NULL) {
|
||||
spin_unlock(&file->table_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
spin_unlock(&file->table_lock);
|
||||
|
||||
drm_gem_handle_delete(file, handle);
|
||||
|
||||
/*
|
||||
* we still have a reference to dma_buf because the ion_handle,
|
||||
* when gem obj handle count become zero and refcount is 1.
|
||||
* It will not release when dma_buf_ops.release is called.
|
||||
* we have to drop it here.
|
||||
* drop the reference on the export fd holds
|
||||
*/
|
||||
|
||||
if (obj->handle_count == 0 &&
|
||||
atomic_read(&obj->refcount.refcount) == 1)
|
||||
drm_gem_object_unreference_unlocked(obj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int meson_drm_gem_open(struct drm_device *dev, struct drm_file *file)
|
||||
{
|
||||
struct drm_meson_file_private *file_priv;
|
||||
|
||||
file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
|
||||
if (!file_priv) {
|
||||
DRM_ERROR("drm_meson file private alloc fail\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
file_priv->client = meson_ion_client_create(-1, "meson-gem");
|
||||
if (!file_priv->client) {
|
||||
DRM_ERROR("open ion client error\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
DRM_DEBUG("open ion client: %p\n", file_priv->client);
|
||||
file->driver_priv = (void *) file_priv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void meson_drm_gem_close(struct drm_device *dev, struct drm_file *file)
|
||||
{
|
||||
struct drm_meson_file_private *file_priv = file->driver_priv;
|
||||
struct ion_client *gem_ion_client = file_priv->client;
|
||||
|
||||
if (gem_ion_client) {
|
||||
DRM_DEBUG(" destroy ion client: %p\n", gem_ion_client);
|
||||
ion_client_destroy(gem_ion_client);
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* drivers/amlogic/drm/meson_gem.h
|
||||
*
|
||||
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MESON_GEM_H
|
||||
#define __MESON_GEM_H
|
||||
#include <drm/drm_gem.h>
|
||||
#include <ion/ion_priv.h>
|
||||
|
||||
struct drm_meson_file_private {
|
||||
struct ion_client *client;
|
||||
};
|
||||
|
||||
struct meson_gem_object {
|
||||
struct drm_gem_object base;
|
||||
struct ion_handle *handle;
|
||||
struct sg_table *sgt;
|
||||
unsigned long size;
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
/* create memory region for drm */
|
||||
int meson_drm_gem_dumb_create(struct drm_file *file_priv,
|
||||
struct drm_device *dev,
|
||||
struct drm_mode_create_dumb *args);
|
||||
|
||||
/* map memory region for drm to user space. */
|
||||
int meson_drm_gem_dumb_map_offset(struct drm_file *file_priv,
|
||||
struct drm_device *dev, uint32_t handle,
|
||||
uint64_t *offset);
|
||||
|
||||
/* free gem object. */
|
||||
void meson_drm_gem_free_object(struct drm_gem_object *gem_obj);
|
||||
|
||||
struct meson_gem_object *meson_drm_gem_init(struct drm_device *dev,
|
||||
unsigned long size);
|
||||
|
||||
int meson_drm_gem_dumb_destroy(struct drm_file *file,
|
||||
struct drm_device *dev,
|
||||
uint32_t handle);
|
||||
|
||||
int meson_drm_gem_open(struct drm_device *dev, struct drm_file *file);
|
||||
void meson_drm_gem_close(struct drm_device *dev, struct drm_file *file);
|
||||
|
||||
#endif /* __MESON_GEM_H */
|
||||
@@ -1,7 +1,7 @@
|
||||
obj-$(CONFIG_AMLOGIC_MEDIA_FB) += fb.o
|
||||
fb-objs = osd_hw.o osd_fb.o osd_debug.o osd_backup.o osd_logo.o osd_io.o
|
||||
fb-objs += osd_antiflicker.o osd_clone.o
|
||||
|
||||
fb-objs += osd_drm.o
|
||||
obj-$(CONFIG_AMLOGIC_MEDIA_FB_OSD_VSYNC_RDMA) += osd_rdma.o
|
||||
|
||||
obj-$(CONFIG_INSTABOOT) += osd_progressbar.o
|
||||
|
||||
@@ -365,8 +365,11 @@ struct hw_para_s {
|
||||
u32 screen_size[HW_OSD_COUNT];
|
||||
char __iomem *screen_base_backup[HW_OSD_COUNT];
|
||||
u32 screen_size_backup[HW_OSD_COUNT];
|
||||
u32 osd_clear[HW_OSD_COUNT];
|
||||
u32 vinfo_width;
|
||||
u32 vinfo_height;
|
||||
u32 fb_drvier_probe;
|
||||
};
|
||||
|
||||
|
||||
#endif /* _OSD_H_ */
|
||||
|
||||
640
drivers/amlogic/media/osd/osd_drm.c
Normal file
640
drivers/amlogic/media/osd/osd_drm.c
Normal file
@@ -0,0 +1,640 @@
|
||||
/*
|
||||
* drivers/amlogic/media/osd/osd_hw.c
|
||||
*
|
||||
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Linux Headers */
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/fdtable.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/ctype.h>
|
||||
|
||||
/* Android Headers */
|
||||
#include <linux/amlogic/cpu_version.h>
|
||||
#include "osd_drm.h"
|
||||
#include "osd_hw.h"
|
||||
#include "osd.h"
|
||||
#include "osd_log.h"
|
||||
|
||||
#define MAX_PLANE 4
|
||||
static struct dentry *osd_debugfs_root;
|
||||
static unsigned int osd_enable[MAX_PLANE];
|
||||
static int plane_osd_id[MAX_PLANE];
|
||||
|
||||
static int parse_para(const char *para, int para_num, int *result)
|
||||
{
|
||||
char *token = NULL;
|
||||
char *params, *params_base;
|
||||
int *out = result;
|
||||
int len = 0, count = 0;
|
||||
int res = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (!para)
|
||||
return 0;
|
||||
|
||||
params = kstrdup(para, GFP_KERNEL);
|
||||
params_base = params;
|
||||
token = params;
|
||||
len = strlen(token);
|
||||
do {
|
||||
token = strsep(¶ms, " ");
|
||||
while (token && (isspace(*token)
|
||||
|| !isgraph(*token)) && len) {
|
||||
token++;
|
||||
len--;
|
||||
}
|
||||
if (len == 0)
|
||||
break;
|
||||
ret = kstrtoint(token, 0, &res);
|
||||
if (ret < 0)
|
||||
break;
|
||||
len = strlen(token);
|
||||
*out++ = res;
|
||||
count++;
|
||||
} while ((token) && (count < para_num) && (len > 0));
|
||||
|
||||
kfree(params_base);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t loglevel_read_file(struct file *file, char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[128];
|
||||
ssize_t len;
|
||||
|
||||
len = snprintf(buf, 128, "%d\n", osd_log_level);
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
|
||||
}
|
||||
|
||||
static ssize_t loglevel_write_file(
|
||||
struct file *file, const char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
unsigned int log_level;
|
||||
char buf[128];
|
||||
int ret = 0;
|
||||
|
||||
count = min_t(size_t, count, (sizeof(buf)-1));
|
||||
if (copy_from_user(buf, userbuf, count))
|
||||
return -EFAULT;
|
||||
buf[count] = 0;
|
||||
ret = kstrtoint(buf, 0, &log_level);
|
||||
osd_log_info("log_level: %d->%d\n", osd_log_level, log_level);
|
||||
osd_log_level = log_level;
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t debug_read_file(struct file *file, char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[1024];
|
||||
char *help;
|
||||
ssize_t len;
|
||||
|
||||
help = osd_get_debug_hw();
|
||||
len = snprintf(buf, strlen(help), "%s", help);
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
|
||||
}
|
||||
|
||||
static ssize_t debug_write_file(struct file *file, const char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[128];
|
||||
|
||||
count = min_t(size_t, count, (sizeof(buf)-1));
|
||||
if (copy_from_user(buf, userbuf, count))
|
||||
return -EFAULT;
|
||||
buf[count] = 0;
|
||||
osd_set_debug_hw(buf);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t osd_display_debug_read_file(struct file *file,
|
||||
char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[128];
|
||||
ssize_t len;
|
||||
u32 osd_display_debug_enable;
|
||||
|
||||
osd_get_display_debug(&osd_display_debug_enable);
|
||||
len = snprintf(buf, 128, "%d\n", osd_display_debug_enable);
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
|
||||
}
|
||||
|
||||
static ssize_t osd_display_debug_write_file(struct file *file,
|
||||
const char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[128];
|
||||
u32 osd_display_debug_enable;
|
||||
int ret = 0;
|
||||
|
||||
count = min_t(size_t, count, (sizeof(buf)-1));
|
||||
if (copy_from_user(buf, userbuf, count))
|
||||
return -EFAULT;
|
||||
buf[count] = 0;
|
||||
ret = kstrtoint(buf, 0, &osd_display_debug_enable);
|
||||
osd_set_display_debug(osd_display_debug_enable);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t reset_status_read_file(struct file *file, char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[128];
|
||||
ssize_t len;
|
||||
unsigned int status;
|
||||
|
||||
status = osd_get_reset_status();
|
||||
len = snprintf(buf, 128, "0x%x\n", status);
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
|
||||
}
|
||||
|
||||
static ssize_t blank_read_file(struct file *file, char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[128];
|
||||
ssize_t len;
|
||||
struct seq_file *s = file->private_data;
|
||||
int osd_id = *(int *)s;
|
||||
|
||||
len = snprintf(buf, 128, "%d\n", osd_enable[osd_id]);
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
|
||||
}
|
||||
|
||||
static ssize_t blank_write_file(struct file *file, const char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[128];
|
||||
struct seq_file *s = file->private_data;
|
||||
int osd_id = *(int *)s;
|
||||
int ret = 0;
|
||||
|
||||
count = min_t(size_t, count, (sizeof(buf)-1));
|
||||
if (copy_from_user(buf, userbuf, count))
|
||||
return -EFAULT;
|
||||
buf[count] = 0;
|
||||
ret = kstrtoint(buf, 0, &osd_enable[osd_id]);
|
||||
if (ret <= 0)
|
||||
return -EFAULT;
|
||||
osd_enable_hw(osd_id, (osd_enable[osd_id] != 0) ? 0 : 1);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t free_scale_read_file(struct file *file, char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[128];
|
||||
ssize_t len;
|
||||
struct seq_file *s = file->private_data;
|
||||
int osd_id = *(int *)s;
|
||||
unsigned int free_scale_enable;
|
||||
|
||||
osd_get_free_scale_enable_hw(osd_id, &free_scale_enable);
|
||||
len = snprintf(buf, PAGE_SIZE, "free_scale_enable:[0x%x]\n",
|
||||
free_scale_enable);
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
|
||||
}
|
||||
|
||||
static ssize_t free_scale_write_file(struct file *file,
|
||||
const char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[128];
|
||||
struct seq_file *s = file->private_data;
|
||||
int osd_id = *(int *)s;
|
||||
unsigned int free_scale_enable;
|
||||
int ret = 0;
|
||||
|
||||
count = min_t(size_t, count, (sizeof(buf)-1));
|
||||
if (copy_from_user(buf, userbuf, count))
|
||||
return -EFAULT;
|
||||
buf[count] = 0;
|
||||
ret = kstrtoint(buf, 0, &free_scale_enable);
|
||||
osd_set_free_scale_enable_hw(osd_id, free_scale_enable);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t free_scale_axis_read_file(struct file *file,
|
||||
char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[128];
|
||||
ssize_t len;
|
||||
struct seq_file *s = file->private_data;
|
||||
int osd_id = *(int *)s;
|
||||
int x, y, w, h;
|
||||
|
||||
osd_get_free_scale_axis_hw(osd_id, &x, &y, &w, &h);
|
||||
len = snprintf(buf, 128, "%d %d %d %d\n", x, y, w, h);
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
|
||||
}
|
||||
|
||||
static ssize_t free_scale_axis_write_file(struct file *file,
|
||||
const char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[128];
|
||||
struct seq_file *s = file->private_data;
|
||||
int osd_id = *(int *)s;
|
||||
int parsed[4];
|
||||
|
||||
count = min_t(size_t, count, (sizeof(buf)-1));
|
||||
if (copy_from_user(buf, userbuf, count))
|
||||
return -EFAULT;
|
||||
buf[count] = 0;
|
||||
if (likely(parse_para(buf, 4, parsed) == 4))
|
||||
osd_set_free_scale_axis_hw(osd_id,
|
||||
parsed[0], parsed[1], parsed[2], parsed[3]);
|
||||
else
|
||||
osd_log_err("set free scale axis error\n");
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t window_axis_read_file(struct file *file,
|
||||
char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[128];
|
||||
ssize_t len;
|
||||
struct seq_file *s = file->private_data;
|
||||
int osd_id = *(int *)s;
|
||||
int x0, y0, x1, y1;
|
||||
|
||||
osd_get_window_axis_hw(osd_id, &x0, &y0, &x1, &y1);
|
||||
len = snprintf(buf, 128, "%d %d %d %d\n",
|
||||
x0, y0, x1, y1);
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
|
||||
}
|
||||
|
||||
static ssize_t window_axis_write_file(struct file *file,
|
||||
const char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[128];
|
||||
struct seq_file *s = file->private_data;
|
||||
int osd_id = *(int *)s;
|
||||
int parsed[4];
|
||||
|
||||
count = min_t(size_t, count, (sizeof(buf)-1));
|
||||
if (copy_from_user(buf, userbuf, count))
|
||||
return -EFAULT;
|
||||
buf[count] = 0;
|
||||
if (likely(parse_para(buf, 4, parsed) == 4))
|
||||
osd_set_window_axis_hw(osd_id,
|
||||
parsed[0], parsed[1], parsed[2], parsed[3]);
|
||||
else
|
||||
osd_log_err("set window axis error\n");
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t osd_reverse_read_file(struct file *file,
|
||||
char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[128];
|
||||
ssize_t len;
|
||||
struct seq_file *s = file->private_data;
|
||||
int osd_id = *(int *)s;
|
||||
char *str[4] = {"NONE", "ALL", "X_REV", "Y_REV"};
|
||||
unsigned int osd_reverse = 0;
|
||||
|
||||
osd_get_reverse_hw(osd_id, &osd_reverse);
|
||||
if (osd_reverse >= REVERSE_MAX)
|
||||
osd_reverse = REVERSE_FALSE;
|
||||
len = snprintf(buf, 128, "osd_reverse:[%s]\n",
|
||||
str[osd_reverse]);
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
|
||||
}
|
||||
|
||||
static ssize_t osd_reverse_write_file(struct file *file,
|
||||
const char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[128];
|
||||
struct seq_file *s = file->private_data;
|
||||
int osd_id = *(int *)s;
|
||||
unsigned int osd_reverse = 0;
|
||||
int ret = 0;
|
||||
|
||||
count = min_t(size_t, count, (sizeof(buf)-1));
|
||||
if (copy_from_user(buf, userbuf, count))
|
||||
return -EFAULT;
|
||||
buf[count] = 0;
|
||||
ret = kstrtoint(buf, 0, &osd_reverse);
|
||||
if (osd_reverse >= REVERSE_MAX)
|
||||
osd_reverse = REVERSE_FALSE;
|
||||
osd_set_reverse_hw(osd_id, osd_reverse);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t osd_order_read_file(struct file *file,
|
||||
char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[128];
|
||||
ssize_t len;
|
||||
struct seq_file *s = file->private_data;
|
||||
int osd_id = *(int *)s;
|
||||
unsigned int order = 0;
|
||||
|
||||
osd_get_order_hw(osd_id, &order);
|
||||
len = snprintf(buf, 128, "order:[0x%x]\n", order);
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
|
||||
}
|
||||
|
||||
static ssize_t osd_order_write_file(struct file *file,
|
||||
const char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[128];
|
||||
struct seq_file *s = file->private_data;
|
||||
int osd_id = *(int *)s;
|
||||
unsigned int order = 0;
|
||||
int ret = 0;
|
||||
|
||||
count = min_t(size_t, count, (sizeof(buf)-1));
|
||||
if (copy_from_user(buf, userbuf, count))
|
||||
return -EFAULT;
|
||||
buf[count] = 0;
|
||||
ret = kstrtoint(buf, 0, &order);
|
||||
osd_set_order_hw(osd_id, order);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t osd_afbcd_read_file(struct file *file,
|
||||
char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[128];
|
||||
ssize_t len;
|
||||
unsigned int enable_afbcd = 0;
|
||||
|
||||
enable_afbcd = osd_get_afbc();
|
||||
len = snprintf(buf, 128, "%d\n", enable_afbcd);
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
|
||||
}
|
||||
|
||||
static ssize_t osd_afbcd_write_file(struct file *file,
|
||||
const char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[128];
|
||||
unsigned int enable_afbcd = 0;
|
||||
int ret = 0;
|
||||
|
||||
count = min_t(size_t, count, (sizeof(buf)-1));
|
||||
if (copy_from_user(buf, userbuf, count))
|
||||
return -EFAULT;
|
||||
buf[count] = 0;
|
||||
ret = kstrtoint(buf, 0, &enable_afbcd);
|
||||
osd_log_info("afbc: %d\n", enable_afbcd);
|
||||
osd_set_afbc(enable_afbcd);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t osd_clear_write_file(struct file *file,
|
||||
const char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[128];
|
||||
struct seq_file *s = file->private_data;
|
||||
int osd_id = *(int *)s;
|
||||
unsigned int osd_clear = 0;
|
||||
int ret = 0;
|
||||
|
||||
count = min_t(size_t, count, (sizeof(buf)-1));
|
||||
if (copy_from_user(buf, userbuf, count))
|
||||
return -EFAULT;
|
||||
buf[count] = 0;
|
||||
ret = kstrtoint(buf, 0, &osd_clear);
|
||||
osd_set_clear(osd_id, osd_clear);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const struct file_operations loglevel_file_ops = {
|
||||
.open = simple_open,
|
||||
.read = loglevel_read_file,
|
||||
.write = loglevel_write_file,
|
||||
};
|
||||
|
||||
static const struct file_operations debug_file_ops = {
|
||||
.open = simple_open,
|
||||
.read = debug_read_file,
|
||||
.write = debug_write_file,
|
||||
};
|
||||
|
||||
static const struct file_operations osd_display_debug_file_ops = {
|
||||
.open = simple_open,
|
||||
.read = osd_display_debug_read_file,
|
||||
.write = osd_display_debug_write_file,
|
||||
};
|
||||
|
||||
static const struct file_operations reset_status_file_ops = {
|
||||
.open = simple_open,
|
||||
.read = reset_status_read_file,
|
||||
};
|
||||
|
||||
static const struct file_operations blank_file_ops = {
|
||||
.open = simple_open,
|
||||
.read = blank_read_file,
|
||||
.write = blank_write_file,
|
||||
};
|
||||
|
||||
static const struct file_operations free_scale_file_ops = {
|
||||
.open = simple_open,
|
||||
.read = free_scale_read_file,
|
||||
.write = free_scale_write_file,
|
||||
};
|
||||
|
||||
static const struct file_operations free_scale_axis_file_ops = {
|
||||
.open = simple_open,
|
||||
.read = free_scale_axis_read_file,
|
||||
.write = free_scale_axis_write_file,
|
||||
};
|
||||
|
||||
static const struct file_operations window_axis_file_ops = {
|
||||
.open = simple_open,
|
||||
.read = window_axis_read_file,
|
||||
.write = window_axis_write_file,
|
||||
};
|
||||
|
||||
static const struct file_operations osd_reverse_file_ops = {
|
||||
.open = simple_open,
|
||||
.read = osd_reverse_read_file,
|
||||
.write = osd_reverse_write_file,
|
||||
};
|
||||
|
||||
static const struct file_operations osd_order_file_ops = {
|
||||
.open = simple_open,
|
||||
.read = osd_order_read_file,
|
||||
.write = osd_order_write_file,
|
||||
};
|
||||
|
||||
static const struct file_operations osd_afbcd_file_ops = {
|
||||
.open = simple_open,
|
||||
.read = osd_afbcd_read_file,
|
||||
.write = osd_afbcd_write_file,
|
||||
};
|
||||
|
||||
static const struct file_operations osd_clear_file_ops = {
|
||||
.open = simple_open,
|
||||
.write = osd_clear_write_file,
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct osd_drm_debugfs_files_s {
|
||||
const char *name;
|
||||
const umode_t mode;
|
||||
const struct file_operations *fops;
|
||||
};
|
||||
|
||||
static struct osd_drm_debugfs_files_s osd_drm_debugfs_files[] = {
|
||||
{"loglevel", S_IFREG | 0640, &loglevel_file_ops},
|
||||
{"debug", S_IFREG | 0640, &debug_file_ops},
|
||||
{"osd_display_debug", S_IFREG | 0640, &osd_display_debug_file_ops},
|
||||
{"reset_status", S_IFREG | 0440, &reset_status_file_ops},
|
||||
{"blank", S_IFREG | 0640, &blank_file_ops},
|
||||
{"free_scale", S_IFREG | 0640, &free_scale_file_ops},
|
||||
{"free_scale_axis", S_IFREG | 0640, &free_scale_axis_file_ops},
|
||||
{"window_axis", S_IFREG | 0640, &window_axis_file_ops},
|
||||
{"osd_reverse", S_IFREG | 0640, &osd_reverse_file_ops},
|
||||
{"order", S_IFREG | 0640, &osd_order_file_ops},
|
||||
{"osd_afbcd", S_IFREG | 0640, &osd_afbcd_file_ops},
|
||||
{"osd_clear", S_IFREG | 0220, &osd_clear_file_ops},
|
||||
};
|
||||
|
||||
void osd_drm_debugfs_add(
|
||||
struct dentry **plane_debugfs_dir,
|
||||
char *name,
|
||||
int osd_id)
|
||||
{
|
||||
struct dentry *ent;
|
||||
int i;
|
||||
|
||||
plane_osd_id[osd_id] = osd_id;
|
||||
*plane_debugfs_dir = debugfs_create_dir(name, osd_debugfs_root);
|
||||
if (!plane_debugfs_dir)
|
||||
osd_log_info("debugfs_create_dir failed: name=%s\n", name);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(osd_drm_debugfs_files); i++) {
|
||||
ent = debugfs_create_file(osd_drm_debugfs_files[i].name,
|
||||
osd_drm_debugfs_files[i].mode,
|
||||
*plane_debugfs_dir, &plane_osd_id[osd_id],
|
||||
osd_drm_debugfs_files[i].fops);
|
||||
if (!ent)
|
||||
osd_log_info("debugfs create failed\n");
|
||||
}
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL(osd_drm_debugfs_add);
|
||||
|
||||
void osd_drm_debugfs_init(void)
|
||||
{
|
||||
osd_debugfs_root = debugfs_create_dir("graphics", NULL);
|
||||
if (!osd_debugfs_root)
|
||||
pr_err("can't create debugfs dir\n");
|
||||
}
|
||||
EXPORT_SYMBOL(osd_drm_debugfs_init);
|
||||
|
||||
void osd_drm_debugfs_exit(void)
|
||||
{
|
||||
debugfs_remove(osd_debugfs_root);
|
||||
}
|
||||
EXPORT_SYMBOL(osd_drm_debugfs_exit);
|
||||
|
||||
void osd_drm_vsync_isr_handler(void)
|
||||
{
|
||||
|
||||
#ifndef CONFIG_AMLOGIC_MEDIA_FB_OSD_VSYNC_RDMA
|
||||
osd_update_scan_mode();
|
||||
/* go through update list */
|
||||
walk_through_update_list();
|
||||
osd_update_3d_mode();
|
||||
osd_update_vsync_hit();
|
||||
osd_hw_reset();
|
||||
#else
|
||||
if (get_cpu_type() != MESON_CPU_MAJOR_ID_AXG)
|
||||
osd_rdma_interrupt_done_clear();
|
||||
else {
|
||||
osd_update_scan_mode();
|
||||
/* go through update list */
|
||||
walk_through_update_list();
|
||||
osd_update_3d_mode();
|
||||
osd_update_vsync_hit();
|
||||
osd_hw_reset();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL(osd_drm_vsync_isr_handler);
|
||||
|
||||
void osd_drm_plane_page_flip(struct osd_plane_map_s *plane_map)
|
||||
{
|
||||
osd_page_flip(plane_map);
|
||||
}
|
||||
EXPORT_SYMBOL(osd_drm_plane_page_flip);
|
||||
|
||||
void osd_drm_plane_enable_hw(u32 index, u32 enable)
|
||||
{
|
||||
osd_enable_hw(index, enable);
|
||||
}
|
||||
EXPORT_SYMBOL(osd_drm_plane_enable_hw);
|
||||
|
||||
int osd_drm_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* osd hw init */
|
||||
ret = osd_io_remap();
|
||||
if (!ret) {
|
||||
osd_log_err("osd_io_remap failed\n");
|
||||
return -1;
|
||||
}
|
||||
/* init osd logo */
|
||||
ret = logo_work_init();
|
||||
if (ret == 0)
|
||||
osd_init_hw(1, 0);
|
||||
else
|
||||
osd_init_hw(0, 0);
|
||||
/* freescale switch from osd2 to osd1*/
|
||||
osd_log_info("freescale switch from osd2 to osd1\n");
|
||||
osd_set_free_scale_mode_hw(OSD2, 1);
|
||||
osd_set_free_scale_enable_hw(OSD2, 0);
|
||||
osd_set_free_scale_mode_hw(OSD1, 1);
|
||||
osd_set_free_scale_axis_hw(OSD1, 0, 0, 1919, 1279);
|
||||
osd_set_window_axis_hw(OSD1, 0, 0, 1919, 1279);
|
||||
osd_set_free_scale_enable_hw(OSD1, 0x10001);
|
||||
osd_enable_hw(OSD1, 1);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(osd_drm_init);
|
||||
52
drivers/amlogic/media/osd/osd_drm.h
Normal file
52
drivers/amlogic/media/osd/osd_drm.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* drivers/amlogic/media/osd/osd_drm.h
|
||||
*
|
||||
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _OSD_DRM_H_
|
||||
#define _OSD_DRM_H_
|
||||
|
||||
struct osd_plane_map_s {
|
||||
u32 plane_index;
|
||||
u32 zorder;
|
||||
u32 phy_addr;
|
||||
u32 format;
|
||||
u32 enable;
|
||||
u32 src_x;
|
||||
u32 src_y;
|
||||
u32 src_w;
|
||||
u32 src_h;
|
||||
u32 dst_x;
|
||||
u32 dst_y;
|
||||
u32 dst_w;
|
||||
u32 dst_h;
|
||||
int byte_stride;
|
||||
u32 reserve;
|
||||
};
|
||||
|
||||
int osd_drm_init(void);
|
||||
void osd_drm_debugfs_add(
|
||||
struct dentry **plane_debugfs_dir,
|
||||
char *name,
|
||||
int osd_id);
|
||||
void osd_drm_debugfs_init(void);
|
||||
void osd_drm_debugfs_exit(void);
|
||||
|
||||
void osd_drm_plane_page_flip(struct osd_plane_map_s *plane_map);
|
||||
void osd_drm_plane_enable_hw(u32 index, u32 enable);
|
||||
void osd_drm_vsync_isr_handler(void);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -2807,9 +2807,9 @@ static int osd_probe(struct platform_device *pdev)
|
||||
/* init osd logo */
|
||||
ret = logo_work_init();
|
||||
if (ret == 0)
|
||||
osd_init_hw(1);
|
||||
osd_init_hw(1, 1);
|
||||
else
|
||||
osd_init_hw(0);
|
||||
osd_init_hw(0, 1);
|
||||
|
||||
/* get buffer size from dt */
|
||||
ret = of_property_read_u32_array(pdev->dev.of_node,
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
|
||||
#include "osd_hw.h"
|
||||
#include "osd_hw_def.h"
|
||||
#include "osd_fb.h"
|
||||
|
||||
#ifdef CONFIG_AMLOGIC_VSYNC_FIQ_ENABLE
|
||||
#define FIQ_VSYNC
|
||||
@@ -83,6 +84,9 @@ static DECLARE_WAIT_QUEUE_HEAD(osd_vsync_wq);
|
||||
static bool vsync_hit;
|
||||
static bool osd_update_window_axis;
|
||||
static int osd_afbc_dec_enable;
|
||||
static u32 extern_canvas[2] = {EXTERN1_CANVAS, EXTERN2_CANVAS};
|
||||
static int ext_canvas_id;
|
||||
|
||||
static void osd_clone_pan(u32 index, u32 yoffset, int debug_flag);
|
||||
static void osd_set_dummy_data(u32 alpha);
|
||||
|
||||
@@ -738,7 +742,7 @@ void osd_update_scan_mode(void)
|
||||
osd_update_interlace_mode(index);
|
||||
}
|
||||
|
||||
static inline void walk_through_update_list(void)
|
||||
void walk_through_update_list(void)
|
||||
{
|
||||
u32 i, j;
|
||||
|
||||
@@ -887,7 +891,6 @@ static int notify_to_amvideo(void)
|
||||
return 0;
|
||||
}
|
||||
/*************** end of GXL/GXM hardware alpha bug workaround ***************/
|
||||
|
||||
#ifdef FIQ_VSYNC
|
||||
static irqreturn_t vsync_isr(int irq, void *dev_id)
|
||||
{
|
||||
@@ -924,7 +927,6 @@ static irqreturn_t vsync_isr(int irq, void *dev_id)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void osd_set_pxp_mode(u32 mode)
|
||||
{
|
||||
pxp_mode = mode;
|
||||
@@ -949,14 +951,16 @@ u32 osd_get_reset_status(void)
|
||||
void osd_wait_vsync_hw(void)
|
||||
{
|
||||
unsigned long timeout;
|
||||
if (osd_hw.fb_drvier_probe) {
|
||||
vsync_hit = false;
|
||||
|
||||
vsync_hit = false;
|
||||
|
||||
if (pxp_mode)
|
||||
timeout = msecs_to_jiffies(50);
|
||||
else
|
||||
timeout = HZ;
|
||||
wait_event_interruptible_timeout(osd_vsync_wq, vsync_hit, timeout);
|
||||
if (pxp_mode)
|
||||
timeout = msecs_to_jiffies(50);
|
||||
else
|
||||
timeout = HZ;
|
||||
wait_event_interruptible_timeout(
|
||||
osd_vsync_wq, vsync_hit, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
s32 osd_wait_vsync_event(void)
|
||||
@@ -2041,6 +2045,7 @@ void osd_set_display_debug(u32 osd_display_debug_enable)
|
||||
osd_hw.osd_display_debug = osd_display_debug_enable;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_AMLOGIC_MEDIA_FB_OSD_SYNC_FENCE
|
||||
enum {
|
||||
HAL_PIXEL_FORMAT_RGBA_8888 = 1,
|
||||
@@ -2050,8 +2055,6 @@ enum {
|
||||
HAL_PIXEL_FORMAT_BGRA_8888 = 5,
|
||||
};
|
||||
|
||||
static u32 extern_canvas[2] = {EXTERN1_CANVAS, EXTERN2_CANVAS};
|
||||
static int ext_canvas_id;
|
||||
static bool use_ext;
|
||||
const struct color_bit_define_s extern_color_format_array[] = {
|
||||
/*32 bit color RGBA */
|
||||
@@ -3918,11 +3921,12 @@ void osd_init_scan_mode(void)
|
||||
}
|
||||
}
|
||||
|
||||
void osd_init_hw(u32 logo_loaded)
|
||||
void osd_init_hw(u32 logo_loaded, u32 osd_probe)
|
||||
{
|
||||
u32 group, idx, data32, data2;
|
||||
int err_num = 0;
|
||||
|
||||
osd_hw.fb_drvier_probe = osd_probe;
|
||||
osd_vpu_power_on();
|
||||
if (get_cpu_type() == MESON_CPU_MAJOR_ID_GXTVBB)
|
||||
backup_regs_init(HW_RESET_AFBCD_REGS);
|
||||
@@ -4081,6 +4085,8 @@ void osd_init_hw(u32 logo_loaded)
|
||||
osd_hw.free_scale_mode[OSD2] = 1;
|
||||
osd_hw.buffer_alloc[OSD1] = 0;
|
||||
osd_hw.buffer_alloc[OSD2] = 0;
|
||||
osd_hw.osd_clear[OSD1] = 0;
|
||||
osd_hw.osd_clear[OSD2] = 0;
|
||||
if ((get_cpu_type() == MESON_CPU_MAJOR_ID_GXM)
|
||||
|| (get_cpu_type() == MESON_CPU_MAJOR_ID_TXLX))
|
||||
osd_reg_write(VPP_OSD_SC_DUMMY_DATA, 0x002020ff);
|
||||
@@ -4103,22 +4109,24 @@ void osd_init_hw(u32 logo_loaded)
|
||||
INIT_LIST_HEAD(&post_fence_list);
|
||||
mutex_init(&post_fence_list_lock);
|
||||
#endif
|
||||
#ifdef FIQ_VSYNC
|
||||
osd_hw.fiq_handle_item.handle = vsync_isr;
|
||||
osd_hw.fiq_handle_item.key = (u32)vsync_isr;
|
||||
osd_hw.fiq_handle_item.name = "osd_vsync";
|
||||
if (register_fiq_bridge_handle(&osd_hw.fiq_handle_item))
|
||||
#else
|
||||
err_num = request_irq(int_viu_vsync, &vsync_isr,
|
||||
IRQF_SHARED, "osd-vsync", osd_setup_hw);
|
||||
if (err_num)
|
||||
#endif
|
||||
osd_log_err("can't request irq for vsync,err_num=%d\n",
|
||||
-err_num);
|
||||
#ifdef FIQ_VSYNC
|
||||
request_fiq(INT_VIU_VSYNC, &osd_fiq_isr);
|
||||
#endif
|
||||
|
||||
if (osd_hw.fb_drvier_probe) {
|
||||
#ifdef FIQ_VSYNC
|
||||
osd_hw.fiq_handle_item.handle = vsync_isr;
|
||||
osd_hw.fiq_handle_item.key = (u32)vsync_isr;
|
||||
osd_hw.fiq_handle_item.name = "osd_vsync";
|
||||
if (register_fiq_bridge_handle(&osd_hw.fiq_handle_item))
|
||||
#else
|
||||
err_num = request_irq(int_viu_vsync, &vsync_isr,
|
||||
IRQF_SHARED, "osd-vsync", osd_setup_hw);
|
||||
if (err_num)
|
||||
#endif
|
||||
osd_log_err("can't request irq for vsync,err_num=%d\n",
|
||||
-err_num);
|
||||
#ifdef FIQ_VSYNC
|
||||
request_fiq(INT_VIU_VSYNC, &osd_fiq_isr);
|
||||
#endif
|
||||
}
|
||||
#ifdef CONFIG_AMLOGIC_MEDIA_FB_OSD_VSYNC_RDMA
|
||||
if (get_cpu_type() != MESON_CPU_MAJOR_ID_AXG)
|
||||
osd_rdma_enable(1);
|
||||
@@ -4370,3 +4378,464 @@ void osd_restore_screen_info(
|
||||
*screen_size = (unsigned long)osd_hw.screen_size[index];
|
||||
|
||||
}
|
||||
|
||||
void osd_set_clear(u32 index, u32 osd_clear)
|
||||
{
|
||||
osd_hw.osd_clear[index] = osd_clear;
|
||||
}
|
||||
|
||||
static const struct color_bit_define_s *convert_panel_format(u32 format)
|
||||
{
|
||||
const struct color_bit_define_s *color = NULL;
|
||||
|
||||
switch (format) {
|
||||
case COLOR_INDEX_02_PAL4:
|
||||
case COLOR_INDEX_04_PAL16:
|
||||
case COLOR_INDEX_08_PAL256:
|
||||
case COLOR_INDEX_16_655:
|
||||
case COLOR_INDEX_16_844:
|
||||
case COLOR_INDEX_16_6442:
|
||||
case COLOR_INDEX_16_4444_R:
|
||||
case COLOR_INDEX_16_4642_R:
|
||||
case COLOR_INDEX_16_1555_A:
|
||||
case COLOR_INDEX_16_4444_A:
|
||||
case COLOR_INDEX_16_565:
|
||||
case COLOR_INDEX_24_6666_A:
|
||||
case COLOR_INDEX_24_6666_R:
|
||||
case COLOR_INDEX_24_8565:
|
||||
case COLOR_INDEX_24_5658:
|
||||
case COLOR_INDEX_24_888_B:
|
||||
case COLOR_INDEX_24_RGB:
|
||||
case COLOR_INDEX_32_BGRX:
|
||||
case COLOR_INDEX_32_XBGR:
|
||||
case COLOR_INDEX_32_RGBX:
|
||||
case COLOR_INDEX_32_XRGB:
|
||||
case COLOR_INDEX_32_BGRA:
|
||||
case COLOR_INDEX_32_ABGR:
|
||||
case COLOR_INDEX_32_RGBA:
|
||||
case COLOR_INDEX_32_ARGB:
|
||||
case COLOR_INDEX_YUV_422:
|
||||
color = &default_color_format_array[format];
|
||||
break;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
static bool osd_direct_render(struct osd_plane_map_s *plane_map)
|
||||
{
|
||||
u32 index = plane_map->plane_index;
|
||||
u32 phy_addr = plane_map->phy_addr;
|
||||
u32 width_src, width_dst, height_src, height_dst;
|
||||
u32 x_start, x_end, y_start, y_end;
|
||||
bool freescale_update = false;
|
||||
struct pandata_s freescale_dst[HW_OSD_COUNT];
|
||||
void *vaddr = NULL;
|
||||
|
||||
phy_addr = phy_addr + plane_map->byte_stride * plane_map->src_y;
|
||||
if (osd_hw.osd_clear[index]) {
|
||||
vaddr = phys_to_virt(phy_addr);
|
||||
if (vaddr)
|
||||
memset(vaddr, 0x0,
|
||||
plane_map->byte_stride*plane_map->src_h);
|
||||
}
|
||||
canvas_config(osd_hw.fb_gem[index].canvas_idx,
|
||||
phy_addr,
|
||||
plane_map->byte_stride,
|
||||
plane_map->src_h,
|
||||
CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
|
||||
|
||||
width_dst = osd_hw.free_dst_data_backup[index].x_end -
|
||||
osd_hw.free_dst_data_backup[index].x_start + 1;
|
||||
width_src = osd_hw.free_src_data_backup[index].x_end -
|
||||
osd_hw.free_src_data_backup[index].x_start + 1;
|
||||
|
||||
height_dst = osd_hw.free_dst_data_backup[index].y_end -
|
||||
osd_hw.free_dst_data_backup[index].y_start + 1;
|
||||
height_src = osd_hw.free_src_data_backup[index].y_end -
|
||||
osd_hw.free_src_data_backup[index].y_start + 1;
|
||||
osd_hw.free_scale_enable[index] = 1;
|
||||
|
||||
if (osd_hw.free_scale_enable[index] ||
|
||||
(width_src != width_dst) ||
|
||||
(height_src != height_dst) ||
|
||||
(plane_map->src_w != plane_map->dst_w) ||
|
||||
(plane_map->src_h != plane_map->dst_h)) {
|
||||
osd_hw.free_scale[index].h_enable = 1;
|
||||
osd_hw.free_scale[index].v_enable = 1;
|
||||
osd_hw.free_scale_enable[index] = 0x10001;
|
||||
osd_hw.free_scale_mode[index] = 1;
|
||||
if (osd_hw.free_scale_enable[index] !=
|
||||
osd_hw.free_scale_enable_backup[index]) {
|
||||
osd_set_scan_mode(OSD1);
|
||||
freescale_update = true;
|
||||
}
|
||||
|
||||
osd_hw.pandata[index].x_start = plane_map->src_x;
|
||||
osd_hw.pandata[index].x_end =
|
||||
plane_map->src_x + plane_map->src_w - 1;
|
||||
osd_hw.pandata[index].y_start = 0;
|
||||
osd_hw.pandata[index].y_end = plane_map->src_h - 1;
|
||||
|
||||
freescale_dst[index].x_start =
|
||||
osd_hw.free_dst_data_backup[index].x_start +
|
||||
(plane_map->dst_x * width_dst) / width_src;
|
||||
freescale_dst[index].x_end =
|
||||
osd_hw.free_dst_data_backup[index].x_start +
|
||||
((plane_map->dst_x + plane_map->dst_w) *
|
||||
width_dst) / width_src - 1;
|
||||
|
||||
freescale_dst[index].y_start =
|
||||
osd_hw.free_dst_data_backup[index].y_start +
|
||||
(plane_map->dst_y * height_dst) / height_src;
|
||||
freescale_dst[index].y_end =
|
||||
osd_hw.free_dst_data_backup[index].y_start +
|
||||
((plane_map->dst_y + plane_map->dst_h) *
|
||||
height_dst) / height_src - 1;
|
||||
if (osd_hw.osd_reverse[OSD1] == REVERSE_TRUE) {
|
||||
x_start = osd_hw.vinfo_width
|
||||
- freescale_dst[index].x_end - 1;
|
||||
y_start = osd_hw.vinfo_height
|
||||
- freescale_dst[index].y_end - 1;
|
||||
x_end = osd_hw.vinfo_width
|
||||
- freescale_dst[index].x_start - 1;
|
||||
y_end = osd_hw.vinfo_height
|
||||
- freescale_dst[index].y_start - 1;
|
||||
freescale_dst[index].x_start = x_start;
|
||||
freescale_dst[index].y_start = y_start;
|
||||
freescale_dst[index].x_end = x_end;
|
||||
freescale_dst[index].y_end = y_end;
|
||||
} else if (osd_hw.osd_reverse[OSD1] == REVERSE_X) {
|
||||
x_start = osd_hw.vinfo_width
|
||||
- freescale_dst[index].x_end - 1;
|
||||
x_end = osd_hw.vinfo_width
|
||||
- freescale_dst[index].x_start - 1;
|
||||
freescale_dst[index].x_start = x_start;
|
||||
freescale_dst[index].x_end = x_end;
|
||||
|
||||
} else if (osd_hw.osd_reverse[OSD1] == REVERSE_Y) {
|
||||
y_start = osd_hw.vinfo_height
|
||||
- freescale_dst[index].y_end - 1;
|
||||
y_end = osd_hw.vinfo_height
|
||||
- freescale_dst[index].y_start - 1;
|
||||
freescale_dst[index].y_start = y_start;
|
||||
freescale_dst[index].y_end = y_end;
|
||||
}
|
||||
if (memcmp(&(osd_hw.free_src_data[index]),
|
||||
&osd_hw.pandata[index],
|
||||
sizeof(struct pandata_s)) != 0 ||
|
||||
memcmp(&(osd_hw.free_dst_data[index]),
|
||||
&freescale_dst[index],
|
||||
sizeof(struct pandata_s)) != 0) {
|
||||
memcpy(&(osd_hw.free_src_data[index]),
|
||||
&osd_hw.pandata[index],
|
||||
sizeof(struct pandata_s));
|
||||
memcpy(&(osd_hw.free_dst_data[index]),
|
||||
&freescale_dst[index],
|
||||
sizeof(struct pandata_s));
|
||||
freescale_update = true;
|
||||
|
||||
if ((height_dst != height_src) ||
|
||||
(width_dst != width_src))
|
||||
osd_set_dummy_data(0);
|
||||
else
|
||||
osd_set_dummy_data(0xff);
|
||||
}
|
||||
osd_log_dbg2("pandata x=%d,x_end=%d,y=%d,y_end=%d\n",
|
||||
osd_hw.pandata[index].x_start,
|
||||
osd_hw.pandata[index].x_end,
|
||||
osd_hw.pandata[index].y_start,
|
||||
osd_hw.pandata[index].y_end);
|
||||
osd_log_dbg2("plane_map:src_x=%d,src_y=%d,src_w=%d,src_h=%d\n",
|
||||
plane_map->src_x,
|
||||
plane_map->src_y,
|
||||
plane_map->src_w,
|
||||
plane_map->src_h);
|
||||
osd_log_dbg2("fence_map:dst_x=%d,dst_y=%d,dst_w=%d,dst_h=%d\n",
|
||||
plane_map->dst_x,
|
||||
plane_map->dst_y,
|
||||
plane_map->dst_w,
|
||||
plane_map->dst_h);
|
||||
} else {
|
||||
osd_hw.pandata[index].x_start = 0;
|
||||
osd_hw.pandata[index].x_end = plane_map->src_w - 1;
|
||||
osd_hw.pandata[index].y_start = 0;
|
||||
osd_hw.pandata[index].y_end = plane_map->src_h - 1;
|
||||
|
||||
osd_hw.dispdata[index].x_start = plane_map->dst_x;
|
||||
osd_hw.dispdata[index].x_end =
|
||||
plane_map->dst_x + plane_map->dst_w - 1;
|
||||
osd_hw.dispdata[index].y_start = plane_map->dst_y;
|
||||
osd_hw.dispdata[index].y_end =
|
||||
plane_map->dst_y + plane_map->dst_h - 1;
|
||||
if (osd_hw.osd_reverse[OSD1] == REVERSE_TRUE) {
|
||||
x_start = osd_hw.vinfo_width
|
||||
- osd_hw.dispdata[index].x_end - 1;
|
||||
y_start = osd_hw.vinfo_height
|
||||
- osd_hw.dispdata[index].y_end - 1;
|
||||
x_end = osd_hw.vinfo_width
|
||||
- osd_hw.dispdata[index].x_start - 1;
|
||||
y_end = osd_hw.vinfo_height
|
||||
- osd_hw.dispdata[index].y_start - 1;
|
||||
osd_hw.dispdata[index].x_start = x_start;
|
||||
osd_hw.dispdata[index].y_start = y_start;
|
||||
osd_hw.dispdata[index].x_end = x_end;
|
||||
osd_hw.dispdata[index].y_end = y_end;
|
||||
} else if (osd_hw.osd_reverse[OSD1] == REVERSE_X) {
|
||||
x_start = osd_hw.vinfo_width
|
||||
- osd_hw.dispdata[index].x_end - 1;
|
||||
x_end = osd_hw.vinfo_width
|
||||
- osd_hw.dispdata[index].x_start - 1;
|
||||
osd_hw.dispdata[index].x_start = x_start;
|
||||
osd_hw.dispdata[index].x_end = x_end;
|
||||
|
||||
} else if (osd_hw.osd_reverse[OSD1] == REVERSE_Y) {
|
||||
y_start = osd_hw.vinfo_height
|
||||
- osd_hw.dispdata[index].y_end - 1;
|
||||
y_end = osd_hw.vinfo_height
|
||||
- osd_hw.dispdata[index].y_start - 1;
|
||||
osd_hw.dispdata[index].y_start = y_start;
|
||||
osd_hw.dispdata[index].y_end = y_end;
|
||||
}
|
||||
}
|
||||
return freescale_update;
|
||||
}
|
||||
|
||||
static void osd_cursor_move(struct osd_plane_map_s *plane_map)
|
||||
{
|
||||
u32 index = plane_map->plane_index;
|
||||
u32 phy_addr = plane_map->phy_addr;
|
||||
u32 x_start, x_end, y_start, y_end;
|
||||
u32 x, y;
|
||||
void *vaddr = NULL;
|
||||
struct pandata_s disp_tmp;
|
||||
struct pandata_s free_dst_data_backup;
|
||||
|
||||
if (index != OSD2)
|
||||
return;
|
||||
phy_addr = phy_addr + plane_map->byte_stride * plane_map->src_y;
|
||||
if (osd_hw.osd_clear[index]) {
|
||||
vaddr = phys_to_virt(phy_addr);
|
||||
if (vaddr)
|
||||
memset(vaddr, 0x0,
|
||||
plane_map->byte_stride*plane_map->src_h);
|
||||
}
|
||||
canvas_config(osd_hw.fb_gem[index].canvas_idx,
|
||||
phy_addr,
|
||||
plane_map->byte_stride,
|
||||
plane_map->src_h,
|
||||
CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR);
|
||||
|
||||
|
||||
osd_hw.pandata[index].x_start = plane_map->src_x;
|
||||
osd_hw.pandata[index].x_end = plane_map->src_w - 1;
|
||||
osd_hw.pandata[index].y_start = plane_map->src_y;
|
||||
osd_hw.pandata[index].y_end = plane_map->src_h - 1;
|
||||
|
||||
if (osd_hw.free_scale_mode[OSD1]) {
|
||||
if (osd_hw.free_scale_enable[OSD1])
|
||||
memcpy(&disp_tmp, &osd_hw.cursor_dispdata[OSD1],
|
||||
sizeof(struct pandata_s));
|
||||
else
|
||||
memcpy(&disp_tmp, &osd_hw.dispdata[OSD1],
|
||||
sizeof(struct pandata_s));
|
||||
} else
|
||||
memcpy(&disp_tmp, &osd_hw.dispdata[OSD1],
|
||||
sizeof(struct pandata_s));
|
||||
memcpy(&free_dst_data_backup,
|
||||
&osd_hw.free_dst_data_backup[OSD1],
|
||||
sizeof(struct pandata_s));
|
||||
#if 0
|
||||
if (osd_hw.free_scale[OSD1].h_enable)
|
||||
free_dst_data_backup.x_end =
|
||||
osd_hw.free_dst_data_backup[OSD1].x_end * 2;
|
||||
if (osd_hw.free_scale[OSD1].v_enable)
|
||||
free_dst_data_backup.y_end =
|
||||
osd_hw.free_dst_data_backup[OSD1].y_end * 2;
|
||||
#endif
|
||||
x = plane_map->dst_x;
|
||||
y = plane_map->dst_y;
|
||||
if (osd_hw.free_src_data_backup[OSD1].x_end > 0 &&
|
||||
free_dst_data_backup.x_end > 0) {
|
||||
x = x * free_dst_data_backup.x_end /
|
||||
osd_hw.free_src_data_backup[OSD1].x_end;
|
||||
}
|
||||
if (osd_hw.free_src_data_backup[OSD1].y_end > 0 &&
|
||||
free_dst_data_backup.y_end > 0) {
|
||||
y = y * free_dst_data_backup.y_end /
|
||||
osd_hw.free_src_data_backup[OSD1].y_end;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use pandata to show a partial cursor when it is at the edge because
|
||||
* the registers can't have negative values and because we need to
|
||||
* manually clip the cursor when it is past the edge. The edge is
|
||||
* hardcoded to the OSD0 area.
|
||||
*/
|
||||
osd_hw.dispdata[OSD2].x_start = x;
|
||||
osd_hw.dispdata[OSD2].y_start = y;
|
||||
if (x < disp_tmp.x_start) {
|
||||
/* if negative position, set osd to 0,y and pan. */
|
||||
if ((disp_tmp.x_start - x) < plane_map->src_w) {
|
||||
osd_hw.pandata[OSD2].x_start = disp_tmp.x_start - x;
|
||||
osd_hw.pandata[OSD2].x_end = plane_map->src_w - 1;
|
||||
}
|
||||
osd_hw.dispdata[OSD2].x_start = 0;
|
||||
} else {
|
||||
osd_hw.pandata[OSD2].x_start = 0;
|
||||
if (x + plane_map->src_w > disp_tmp.x_end) {
|
||||
/*
|
||||
* if past positive edge,
|
||||
* set osd to inside of the edge and pan.
|
||||
*/
|
||||
if (x < disp_tmp.x_end)
|
||||
osd_hw.pandata[OSD2].x_end = disp_tmp.x_end - x;
|
||||
} else
|
||||
osd_hw.pandata[OSD2].x_end = plane_map->src_w - 1;
|
||||
}
|
||||
if (y < disp_tmp.y_start) {
|
||||
if ((disp_tmp.y_start - y) < plane_map->src_h) {
|
||||
osd_hw.pandata[OSD2].y_start = disp_tmp.y_start - y;
|
||||
osd_hw.pandata[OSD2].y_end = plane_map->src_h - 1;
|
||||
}
|
||||
osd_hw.dispdata[OSD2].y_start = 0;
|
||||
} else {
|
||||
osd_hw.pandata[OSD2].y_start = 0;
|
||||
if (y + plane_map->src_h > disp_tmp.y_end) {
|
||||
if (y < disp_tmp.y_end)
|
||||
osd_hw.pandata[OSD2].y_end = disp_tmp.y_end - y;
|
||||
} else
|
||||
osd_hw.pandata[OSD2].y_end = plane_map->src_h - 1;
|
||||
}
|
||||
osd_hw.dispdata[OSD2].x_end = osd_hw.dispdata[OSD2].x_start +
|
||||
osd_hw.pandata[OSD2].x_end - osd_hw.pandata[OSD2].x_start;
|
||||
osd_hw.dispdata[OSD2].y_end = osd_hw.dispdata[OSD2].y_start +
|
||||
osd_hw.pandata[OSD2].y_end - osd_hw.pandata[OSD2].y_start;
|
||||
|
||||
if (osd_hw.osd_reverse[OSD2] == REVERSE_TRUE) {
|
||||
x_start = osd_hw.vinfo_width
|
||||
- osd_hw.dispdata[index].x_end - 1;
|
||||
y_start = osd_hw.vinfo_height
|
||||
- osd_hw.dispdata[index].y_end - 1;
|
||||
x_end = osd_hw.vinfo_width
|
||||
- osd_hw.dispdata[index].x_start - 1;
|
||||
y_end = osd_hw.vinfo_height
|
||||
- osd_hw.dispdata[index].y_start - 1;
|
||||
osd_hw.dispdata[index].x_start = x_start;
|
||||
osd_hw.dispdata[index].y_start = y_start;
|
||||
osd_hw.dispdata[index].x_end = x_end;
|
||||
osd_hw.dispdata[index].y_end = y_end;
|
||||
} else if (osd_hw.osd_reverse[OSD2] == REVERSE_X) {
|
||||
x_start = osd_hw.vinfo_width
|
||||
- osd_hw.dispdata[index].x_end - 1;
|
||||
x_end = osd_hw.vinfo_width
|
||||
- osd_hw.dispdata[index].x_start - 1;
|
||||
osd_hw.dispdata[index].x_start = x_start;
|
||||
osd_hw.dispdata[index].x_end = x_end;
|
||||
} else if (osd_hw.osd_reverse[OSD2] == REVERSE_Y) {
|
||||
y_start = osd_hw.vinfo_height
|
||||
- osd_hw.dispdata[index].y_end - 1;
|
||||
y_end = osd_hw.vinfo_height
|
||||
- osd_hw.dispdata[index].y_start - 1;
|
||||
osd_hw.dispdata[index].y_start = y_start;
|
||||
osd_hw.dispdata[index].y_end = y_end;
|
||||
}
|
||||
osd_log_dbg2("plane_map:src_x=%d,src_y=%d,src_w=%d,src_h=%d\n",
|
||||
plane_map->src_x,
|
||||
plane_map->src_y,
|
||||
plane_map->src_w,
|
||||
plane_map->src_h);
|
||||
osd_log_dbg2("fence_map:dst_x=%d,dst_y=%d,dst_w=%d,dst_h=%d\n",
|
||||
plane_map->dst_x,
|
||||
plane_map->dst_y,
|
||||
plane_map->dst_w,
|
||||
plane_map->dst_h);
|
||||
osd_log_dbg2("cursor pandata x=%d,x_end=%d,y=%d,y_end=%d\n",
|
||||
osd_hw.pandata[index].x_start,
|
||||
osd_hw.pandata[index].x_end,
|
||||
osd_hw.pandata[index].y_start,
|
||||
osd_hw.pandata[index].y_end);
|
||||
osd_log_dbg2("cursor dispdata x=%d,x_end=%d,y=%d,y_end=%d\n",
|
||||
osd_hw.dispdata[index].x_start,
|
||||
osd_hw.dispdata[index].x_end,
|
||||
osd_hw.dispdata[index].y_start,
|
||||
osd_hw.dispdata[index].y_end);
|
||||
}
|
||||
|
||||
void osd_page_flip(struct osd_plane_map_s *plane_map)
|
||||
{
|
||||
u32 index = plane_map->plane_index;
|
||||
const struct color_bit_define_s *color = NULL;
|
||||
bool freescale_update = false;
|
||||
u32 osd_enable = 0;
|
||||
const struct vinfo_s *vinfo;
|
||||
|
||||
if (index >= 2)
|
||||
return;
|
||||
|
||||
osd_hw.buffer_alloc[index] = 1;
|
||||
if (osd_hw.osd_fps_start)
|
||||
osd_hw.osd_fps++;
|
||||
|
||||
osd_enable = (plane_map->enable & 1) ? ENABLE : DISABLE;
|
||||
vinfo = get_current_vinfo();
|
||||
if (vinfo) {
|
||||
osd_hw.vinfo_width = vinfo->width;
|
||||
osd_hw.vinfo_height = vinfo->height;
|
||||
}
|
||||
if (plane_map->phy_addr && plane_map->src_w
|
||||
&& plane_map->src_h && index == OSD1) {
|
||||
osd_hw.fb_gem[index].canvas_idx =
|
||||
extern_canvas[ext_canvas_id];
|
||||
ext_canvas_id ^= 1;
|
||||
color = convert_panel_format(plane_map->format);
|
||||
if (color) {
|
||||
osd_hw.color_info[index] = color;
|
||||
} else
|
||||
osd_log_err("fence color format error %d\n",
|
||||
plane_map->format);
|
||||
|
||||
freescale_update = osd_direct_render(plane_map);
|
||||
|
||||
if (index == OSD1 &&
|
||||
osd_hw.osd_afbcd[index].enable == ENABLE)
|
||||
osd_hw.osd_afbcd[index].phy_addr =
|
||||
plane_map->phy_addr;
|
||||
osd_hw.reg[index][OSD_COLOR_MODE].update_func();
|
||||
osd_hw.reg[index][DISP_GEOMETRY].update_func();
|
||||
if ((osd_hw.free_scale_enable[index]
|
||||
&& osd_update_window_axis)
|
||||
|| freescale_update) {
|
||||
if (!osd_hw.osd_display_debug)
|
||||
osd_hw.reg[index][DISP_FREESCALE_ENABLE]
|
||||
.update_func();
|
||||
osd_update_window_axis = false;
|
||||
}
|
||||
if ((osd_hw.osd_afbcd[index].enable == DISABLE)
|
||||
&& (osd_enable != osd_hw.enable[index])) {
|
||||
osd_hw.enable[index] = osd_enable;
|
||||
if (!osd_hw.osd_display_debug)
|
||||
osd_hw.reg[index][OSD_ENABLE]
|
||||
.update_func();
|
||||
}
|
||||
osd_wait_vsync_hw();
|
||||
} else if (plane_map->phy_addr && plane_map->src_w
|
||||
&& plane_map->src_h && index == OSD2) {
|
||||
color = convert_panel_format(plane_map->format);
|
||||
if (color) {
|
||||
osd_hw.color_info[index] = color;
|
||||
} else
|
||||
osd_log_err("fence color format error %d\n",
|
||||
plane_map->format);
|
||||
osd_cursor_move(plane_map);
|
||||
osd_hw.reg[index][OSD_COLOR_MODE].update_func();
|
||||
osd_hw.reg[index][DISP_GEOMETRY].update_func();
|
||||
if (osd_enable != osd_hw.enable[index]) {
|
||||
osd_hw.enable[index] = osd_enable;
|
||||
if (!osd_hw.osd_display_debug)
|
||||
osd_hw.reg[index][OSD_ENABLE]
|
||||
.update_func();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <linux/amlogic/media/vout/vinfo.h>
|
||||
#include "osd.h"
|
||||
#include "osd_sync.h"
|
||||
#include "osd_drm.h"
|
||||
|
||||
#define REG_OFFSET (0x20)
|
||||
#define OSD_RELATIVE_BITS 0x33330
|
||||
@@ -125,7 +126,7 @@ extern void osd_init_scan_mode(void);
|
||||
extern void osd_suspend_hw(void);
|
||||
extern void osd_resume_hw(void);
|
||||
extern void osd_shutdown_hw(void);
|
||||
extern void osd_init_hw(u32 logo_loaded);
|
||||
extern void osd_init_hw(u32 logo_loaded, u32 osd_probe);
|
||||
extern void osd_init_scan_mode(void);
|
||||
extern void osd_set_logo_index(int index);
|
||||
extern int osd_get_logo_index(void);
|
||||
@@ -167,4 +168,8 @@ void osd_restore_screen_info(
|
||||
u32 index,
|
||||
char __iomem **screen_base,
|
||||
unsigned long *screen_size);
|
||||
void osd_set_clear(u32 index, u32 osd_clear);
|
||||
void osd_page_flip(struct osd_plane_map_s *plane_map);
|
||||
void walk_through_update_list(void);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user