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:
sky zhou
2017-09-26 19:56:13 +08:00
committed by Pengcheng Chen
parent df67f6cb1a
commit 93c8398efc
24 changed files with 2717 additions and 534 deletions

View File

@@ -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/*

View File

@@ -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

View File

@@ -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.

View File

@@ -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/

View 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);
}

View 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;
}

View 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

View 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

View 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 */

View 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;
}

View File

@@ -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 */

View 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;
}

View File

@@ -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);
}

View File

@@ -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");

View File

@@ -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,

View File

@@ -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);
}
}

View File

@@ -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 */

View File

@@ -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

View File

@@ -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_ */

View 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(&params, " ");
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);

View 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

View File

@@ -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,

View File

@@ -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();
}
}
}

View File

@@ -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