1057 lines
26 KiB
C
1057 lines
26 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/of_graph.h>
|
|
#include <linux/dma-map-ops.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_reserved_mem.h>
|
|
#include <uapi/linux/sched/types.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/string.h>
|
|
#include <linux/notifier.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/cma.h>
|
|
#include <linux/of_address.h>
|
|
|
|
#include <drm/drmP.h>
|
|
#include <drm/drm_atomic.h>
|
|
#include <drm/drm_atomic_helper.h>
|
|
#include <drm/drm_probe_helper.h>
|
|
#include <drm/drm_atomic_uapi.h>
|
|
#include <drm/drm_flip_work.h>
|
|
#include <drm/drm_crtc_helper.h>
|
|
#include <drm/drm_plane_helper.h>
|
|
#include <drm/drm_gem_framebuffer_helper.h>
|
|
#include <drm/drm_rect.h>
|
|
#include <drm/drm_fb_helper.h>
|
|
#include <drm/drm_blend.h>
|
|
#include <drm/drm_gem_dma_helper.h>
|
|
#include <drm/drm_fb_dma_helper.h>
|
|
|
|
#ifdef CONFIG_AMLOGIC_DRM_USE_ION
|
|
#include "meson_gem.h"
|
|
#include "meson_fb.h"
|
|
#endif
|
|
#include "meson_vpu.h"
|
|
#include "meson_vpu_pipeline.h"
|
|
#include "meson_crtc.h"
|
|
#include "meson_logo.h"
|
|
#include "meson_hdmi.h"
|
|
#include "meson_plane.h"
|
|
|
|
#ifdef CONFIG_AMLOGIC_MEDIA_FB
|
|
#include <linux/amlogic/media/osd/osd_logo.h>
|
|
#endif
|
|
#include <linux/amlogic/media/vout/vout_notify.h>
|
|
#include <linux/amlogic/gki_module.h>
|
|
#include <linux/amlogic/aml_free_reserved.h>
|
|
|
|
#ifdef CONFIG_CMA
|
|
struct cma *cma_logo;
|
|
#endif
|
|
|
|
static char *strmode;
|
|
struct am_meson_logo logo[MESON_MAX_CRTC];
|
|
#define NUM_CONNECTORS_LOGO 1
|
|
static struct platform_device *gp_dev;
|
|
static unsigned long gem_mem_start, gem_mem_size;
|
|
static struct resource osd_mem_res;
|
|
bool is_cma;
|
|
|
|
#ifndef CONFIG_AMLOGIC_MEDIA_FB
|
|
static u32 drm_logo_bpp = 16;
|
|
static u32 drm_logo_width = 1920;
|
|
static u32 drm_logo_height = 1080;
|
|
|
|
static int drm_logo_bpp_setup(char *str)
|
|
{
|
|
int ret;
|
|
|
|
ret = kstrtoint(str, 0, &drm_logo_bpp);
|
|
if (ret)
|
|
return ret;
|
|
return 1;
|
|
}
|
|
__setup("display_bpp=", drm_logo_bpp_setup);
|
|
|
|
static u32 drm_logo_get_display_bpp(void)
|
|
{
|
|
return drm_logo_bpp;
|
|
}
|
|
|
|
static int drm_logo_width_setup(char *str)
|
|
{
|
|
int ret;
|
|
|
|
ret = kstrtoint(str, 0, &drm_logo_width);
|
|
if (ret)
|
|
return ret;
|
|
return 1;
|
|
}
|
|
__setup("fb_width=", drm_logo_width_setup);
|
|
|
|
static u32 drm_logo_get_fb_width(void)
|
|
{
|
|
return drm_logo_width;
|
|
}
|
|
|
|
static int drm_logo_height_setup(char *str)
|
|
{
|
|
int ret;
|
|
|
|
ret = kstrtoint(str, 0, &drm_logo_height);
|
|
if (ret)
|
|
return ret;
|
|
return 1;
|
|
}
|
|
__setup("fb_height=", drm_logo_height_setup);
|
|
|
|
static u32 drm_logo_get_fb_height(void)
|
|
{
|
|
return drm_logo_height;
|
|
}
|
|
|
|
#define OSD_INVALID_INFO 0xffffffff
|
|
#define OSD_FIRST_GROUP_START 1
|
|
#define OSD_SECOND_GROUP_START 4
|
|
#define OSD_END 7
|
|
|
|
static inline int str2lower(char *str)
|
|
{
|
|
while (*str != '\0') {
|
|
*str = tolower(*str);
|
|
str++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static struct osd_info_s osd_info = {
|
|
.index = 0,
|
|
.osd_reverse = 0,
|
|
};
|
|
|
|
static struct para_osd_info_s para_osd_info[OSD_END + 2] = {
|
|
/* head */
|
|
{
|
|
"head", OSD_INVALID_INFO,
|
|
OSD_END + 1, 1,
|
|
0, OSD_END + 1
|
|
},
|
|
/* dev */
|
|
{
|
|
"osd0", DEV_OSD0,
|
|
OSD_FIRST_GROUP_START - 1, OSD_FIRST_GROUP_START + 1,
|
|
OSD_FIRST_GROUP_START, OSD_SECOND_GROUP_START - 1
|
|
},
|
|
{
|
|
"osd1", DEV_OSD1,
|
|
OSD_FIRST_GROUP_START, OSD_FIRST_GROUP_START + 2,
|
|
OSD_FIRST_GROUP_START, OSD_SECOND_GROUP_START - 1
|
|
},
|
|
{
|
|
"all", DEV_ALL,
|
|
OSD_FIRST_GROUP_START + 1, OSD_FIRST_GROUP_START + 3,
|
|
OSD_FIRST_GROUP_START, OSD_SECOND_GROUP_START - 1
|
|
},
|
|
/* reverse_mode */
|
|
{
|
|
"false", REVERSE_FALSE,
|
|
OSD_SECOND_GROUP_START - 1, OSD_SECOND_GROUP_START + 1,
|
|
OSD_SECOND_GROUP_START, OSD_END
|
|
},
|
|
{
|
|
"true", REVERSE_TRUE,
|
|
OSD_SECOND_GROUP_START, OSD_SECOND_GROUP_START + 2,
|
|
OSD_SECOND_GROUP_START, OSD_END
|
|
},
|
|
{
|
|
"x_rev", REVERSE_X,
|
|
OSD_SECOND_GROUP_START + 1, OSD_SECOND_GROUP_START + 3,
|
|
OSD_SECOND_GROUP_START, OSD_END
|
|
},
|
|
{
|
|
"y_rev", REVERSE_Y,
|
|
OSD_SECOND_GROUP_START + 2, OSD_SECOND_GROUP_START + 4,
|
|
OSD_SECOND_GROUP_START, OSD_END
|
|
},
|
|
{
|
|
"tail", OSD_INVALID_INFO, OSD_END,
|
|
0, 0,
|
|
OSD_END + 1
|
|
},
|
|
};
|
|
|
|
static inline int install_osd_reverse_info(struct osd_info_s *init_osd_info,
|
|
char *para)
|
|
{
|
|
u32 i = 0;
|
|
static u32 tail = OSD_END + 1;
|
|
u32 first = para_osd_info[0].next_idx;
|
|
|
|
for (i = first; i < tail; i = para_osd_info[i].next_idx) {
|
|
if (strcmp(para_osd_info[i].name, para) == 0) {
|
|
u32 group_start = para_osd_info[i].cur_group_start;
|
|
u32 group_end = para_osd_info[i].cur_group_end;
|
|
u32 prev = para_osd_info[group_start].prev_idx;
|
|
u32 next = para_osd_info[group_end].next_idx;
|
|
|
|
switch (para_osd_info[i].cur_group_start) {
|
|
case OSD_FIRST_GROUP_START:
|
|
init_osd_info->index = para_osd_info[i].info;
|
|
break;
|
|
case OSD_SECOND_GROUP_START:
|
|
init_osd_info->osd_reverse =
|
|
para_osd_info[i].info;
|
|
break;
|
|
}
|
|
para_osd_info[prev].next_idx = next;
|
|
para_osd_info[next].prev_idx = prev;
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int drm_logo_reverse_setup(char *str)
|
|
{
|
|
char *ptr = str;
|
|
char sep[2];
|
|
char *option;
|
|
int count = 2;
|
|
char find = 0;
|
|
struct osd_info_s *init_osd_info;
|
|
|
|
if (!str)
|
|
return -EINVAL;
|
|
|
|
init_osd_info = &osd_info;
|
|
memset(init_osd_info, 0, sizeof(struct osd_info_s));
|
|
do {
|
|
if (!isalpha(*ptr) && !isdigit(*ptr)) {
|
|
find = 1;
|
|
break;
|
|
}
|
|
} while (*++ptr != '\0');
|
|
if (!find)
|
|
return -EINVAL;
|
|
sep[0] = *ptr;
|
|
sep[1] = '\0';
|
|
while ((count--) && (option = strsep(&str, sep))) {
|
|
str2lower(option);
|
|
install_osd_reverse_info(init_osd_info, option);
|
|
}
|
|
return 1;
|
|
}
|
|
__setup("osd_reverse=", drm_logo_reverse_setup);
|
|
|
|
void drm_logo_get_osd_reverse(u32 *index, u32 *reverse_type)
|
|
{
|
|
*index = osd_info.index;
|
|
*reverse_type = osd_info.osd_reverse;
|
|
}
|
|
|
|
#endif
|
|
|
|
struct para_pair_s {
|
|
char *name;
|
|
int value;
|
|
};
|
|
|
|
#ifdef CONFIG_64BIT
|
|
static void free_reserved_highmem(unsigned long start, unsigned long end)
|
|
{
|
|
}
|
|
#else
|
|
static void free_reserved_highmem(unsigned long start, unsigned long end)
|
|
{
|
|
for (; start < end; ) {
|
|
free_highmem_page(phys_to_page(start));
|
|
start += PAGE_SIZE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void free_reserved_mem(unsigned long start, unsigned long size)
|
|
{
|
|
unsigned long end = PAGE_ALIGN(start + size);
|
|
struct page *page, *epage;
|
|
|
|
pr_info("%s %d logo start_addr=%lx, end=%lx\n", __func__, __LINE__, start, end);
|
|
page = phys_to_page(start);
|
|
if (PageHighMem(page)) {
|
|
free_reserved_highmem(start, end);
|
|
} else {
|
|
epage = phys_to_page(end);
|
|
if (!PageHighMem(epage)) {
|
|
aml_free_reserved_area(__va(start),
|
|
__va(end), 0, "fb-memory");
|
|
} else {
|
|
/* reserved area cross zone */
|
|
struct zone *zone;
|
|
unsigned long bound;
|
|
|
|
zone = page_zone(page);
|
|
bound = zone_end_pfn(zone);
|
|
aml_free_reserved_area(__va(start),
|
|
__va(bound << PAGE_SHIFT),
|
|
0, "fb-memory");
|
|
zone = page_zone(epage);
|
|
bound = zone->zone_start_pfn;
|
|
free_reserved_highmem(bound << PAGE_SHIFT, end);
|
|
}
|
|
}
|
|
}
|
|
|
|
void am_meson_free_logo_memory(void)
|
|
{
|
|
if (is_cma) {
|
|
phys_addr_t logo_addr = page_to_phys(logo[VPP0].logo_page);
|
|
|
|
if (logo[VPP0].size > 0 && logo[VPP0].alloc_flag) {
|
|
#ifdef CONFIG_CMA
|
|
DRM_INFO("%s, free cma memory: addr:0x%pa,size:0x%x\n",
|
|
__func__, &logo_addr, logo[VPP0].size);
|
|
|
|
cma_release(cma_logo, logo[VPP0].logo_page, logo[VPP0].size >> PAGE_SHIFT);
|
|
#endif
|
|
}
|
|
} else {
|
|
free_reserved_mem(logo[VPP0].start, logo[VPP0].size);
|
|
DRM_INFO("%s, free none_cma memory: addr:0x%pa,size:0x%x\n",
|
|
__func__, &logo[VPP0].start, logo[VPP0].size);
|
|
}
|
|
|
|
logo[VPP0].alloc_flag = 0;
|
|
logo[VPP0].is_std = 0;
|
|
logo[VPP0].plane_has_fb = 0;
|
|
}
|
|
|
|
void am_meson_drm_put_logo_fb(struct drm_device *dev,
|
|
int index, int uboot_mode_init)
|
|
{
|
|
struct meson_vpu_sub_pipeline *sub_pipe;
|
|
struct meson_drm *private;
|
|
|
|
private = dev->dev_private;
|
|
sub_pipe = private->pipeline->subs[index];
|
|
if (sub_pipe->logo_fb && !uboot_mode_init) {
|
|
DRM_INFO("%s, logo_fb[id:%d,ref:%d]\n", __func__,
|
|
sub_pipe->logo_fb->base.base.id,
|
|
kref_read(&sub_pipe->logo_fb->base.base.refcount));
|
|
drm_framebuffer_put(&sub_pipe->logo_fb->base);
|
|
sub_pipe->logo_fb = NULL;
|
|
}
|
|
}
|
|
|
|
static int am_meson_logo_info_update(struct meson_drm *priv)
|
|
{
|
|
if (is_cma)
|
|
logo[VPP0].start = page_to_phys(logo[VPP0].logo_page);
|
|
|
|
logo[VPP0].alloc_flag = 1;
|
|
/*config 1080p logo as default*/
|
|
if (!logo[VPP0].width || !logo[VPP0].height) {
|
|
logo[VPP0].width = 1920;
|
|
logo[VPP0].height = 1080;
|
|
}
|
|
if (!logo[VPP0].bpp)
|
|
logo[VPP0].bpp = 16;
|
|
if (!logo[VPP0].outputmode_t) {
|
|
strcpy(logo[VPP0].outputmode, "1080p60hz");
|
|
} else {
|
|
strncpy(logo[VPP0].outputmode, logo[VPP0].outputmode_t, VMODE_NAME_LEN_MAX);
|
|
logo[VPP0].outputmode[VMODE_NAME_LEN_MAX - 1] = '\0';
|
|
}
|
|
priv->logo = &logo[VPP0];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int am_meson_logo_init_fb(struct drm_device *dev,
|
|
struct drm_framebuffer *fb, int idx)
|
|
{
|
|
struct am_meson_fb *meson_fb;
|
|
struct am_meson_logo *slogo = &logo[idx];
|
|
struct meson_drm *priv = dev->dev_private;
|
|
|
|
memcpy(slogo, &logo[VPP0], sizeof(struct am_meson_logo));
|
|
if (idx == VPP0) {
|
|
#ifdef CONFIG_AMLOGIC_VOUT_SERVE
|
|
strscpy(slogo->outputmode, get_vout_mode_uboot(),
|
|
sizeof(slogo->outputmode));
|
|
#endif
|
|
} else if (idx == VPP1) {
|
|
#ifdef CONFIG_AMLOGIC_VOUT2_SERVE
|
|
strscpy(slogo->outputmode, get_vout2_mode_uboot(),
|
|
sizeof(slogo->outputmode));
|
|
#endif
|
|
} else if (idx == VPP2) {
|
|
#ifdef CONFIG_AMLOGIC_VOUT3_SERVE
|
|
strscpy(slogo->outputmode, get_vout3_mode_uboot(),
|
|
sizeof(slogo->outputmode));
|
|
#endif
|
|
}
|
|
|
|
slogo->logo_page = logo[VPP0].logo_page;
|
|
slogo->vaddr = logo[VPP0].vaddr;
|
|
slogo->start = logo[VPP0].start;
|
|
slogo->panel_index = priv->primary_plane_index[idx];
|
|
slogo->vpp_index = idx;
|
|
|
|
DRM_INFO("logo%d w=%d,h=%d,start_addr=0x%pa,size=%d\n",
|
|
idx, slogo->width, slogo->height, &slogo->start, slogo->size);
|
|
DRM_DEBUG("bpp=%d,alloc_flag=%d, osd_reverse=%d\n",
|
|
slogo->bpp, slogo->alloc_flag, slogo->osd_reverse);
|
|
DRM_DEBUG("outputmode=%s\n", slogo->outputmode);
|
|
|
|
meson_fb = to_am_meson_fb(fb);
|
|
meson_fb->logo = slogo;
|
|
|
|
if (!strcmp("null", slogo->outputmode) ||
|
|
!strcmp("dummy_l", slogo->outputmode)) {
|
|
DRM_DEBUG("NULL MODE or DUMMY MODE, nothing to do.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*copy from update_output_state,
|
|
*TODO:sync with update_output_state
|
|
*/
|
|
static int am_meson_update_output_state(struct drm_atomic_state *state,
|
|
struct drm_mode_set *set)
|
|
{
|
|
struct drm_crtc *crtc;
|
|
struct drm_crtc_state *new_crtc_state;
|
|
struct drm_connector *connector;
|
|
struct drm_connector_state *new_conn_state;
|
|
int ret, i;
|
|
|
|
/* First disable all connectors on the target crtc. */
|
|
ret = drm_atomic_add_affected_connectors(state, set->crtc);
|
|
if (ret)
|
|
return ret;
|
|
|
|
for_each_new_connector_in_state(state, connector, new_conn_state, i) {
|
|
if (new_conn_state->crtc == set->crtc) {
|
|
ret = drm_atomic_set_crtc_for_connector(new_conn_state,
|
|
NULL);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Make sure legacy setCrtc always re-trains */
|
|
new_conn_state->link_status = DRM_LINK_STATUS_GOOD;
|
|
}
|
|
}
|
|
|
|
/* Then set all connectors from set->connectors on the target crtc */
|
|
for (i = 0; i < set->num_connectors; i++) {
|
|
new_conn_state =
|
|
drm_atomic_get_connector_state(state,
|
|
set->connectors[i]);
|
|
if (IS_ERR(new_conn_state))
|
|
return PTR_ERR(new_conn_state);
|
|
|
|
ret = drm_atomic_set_crtc_for_connector(new_conn_state,
|
|
set->crtc);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
|
|
/* Don't update ->enable for the CRTC in the set_config request,
|
|
* since a mismatch would indicate a bug in the upper layers.
|
|
* The actual modeset code later on will catch any
|
|
* inconsistencies here.
|
|
*/
|
|
if (crtc == set->crtc)
|
|
continue;
|
|
|
|
if (!new_crtc_state->connector_mask) {
|
|
ret = drm_atomic_set_mode_prop_for_crtc(new_crtc_state,
|
|
NULL);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
new_crtc_state->active = false;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _am_meson_occupy_plane_config(struct drm_atomic_state *state,
|
|
struct drm_mode_set *set)
|
|
{
|
|
struct drm_crtc *crtc = set->crtc;
|
|
struct meson_drm *private = crtc->dev->dev_private;
|
|
struct am_osd_plane *osd_plane;
|
|
struct drm_plane_state *plane_state;
|
|
int i, hdisplay, vdisplay, ret;
|
|
|
|
for (i = 0; i < MESON_MAX_OSD; i++) {
|
|
osd_plane = private->osd_planes[i];
|
|
if (!osd_plane || osd_plane->osd_occupied)
|
|
break;
|
|
}
|
|
|
|
if (!osd_plane || !osd_plane->osd_occupied)
|
|
return 0;
|
|
|
|
plane_state = drm_atomic_get_plane_state(state, &osd_plane->base);
|
|
if (IS_ERR(plane_state))
|
|
return PTR_ERR(plane_state);
|
|
|
|
ret = drm_atomic_set_crtc_for_plane(plane_state, crtc);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
drm_mode_get_hv_timing(set->mode, &hdisplay, &vdisplay);
|
|
drm_atomic_set_fb_for_plane(plane_state, set->fb);
|
|
plane_state->crtc_x = 0;
|
|
plane_state->crtc_y = 0;
|
|
plane_state->crtc_w = hdisplay;
|
|
plane_state->crtc_h = vdisplay;
|
|
plane_state->src_x = 0;
|
|
plane_state->src_y = 0;
|
|
plane_state->src_w = 1280/*set->fb->width*/ << 16;
|
|
plane_state->src_h = 720/*set->fb->height*/ << 16;
|
|
plane_state->alpha = 1;
|
|
plane_state->zpos = 128;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int am_meson_drm_set_config(struct drm_mode_set *set,
|
|
struct drm_atomic_state *state, int idx)
|
|
{
|
|
struct drm_crtc_state *crtc_state;
|
|
struct drm_plane_state *plane_state;
|
|
struct drm_crtc *crtc = set->crtc;
|
|
struct meson_drm *private = crtc->dev->dev_private;
|
|
struct am_meson_crtc_state *meson_crtc_state;
|
|
struct am_osd_plane *osd_plane;
|
|
struct am_meson_fb *meson_fb = NULL;
|
|
int hdisplay, vdisplay, ret;
|
|
unsigned int zpos = OSD_PLANE_BEGIN_ZORDER;
|
|
|
|
crtc_state = drm_atomic_get_crtc_state(state, crtc);
|
|
if (IS_ERR(crtc_state))
|
|
return PTR_ERR(crtc_state);
|
|
|
|
meson_crtc_state = to_am_meson_crtc_state(crtc_state);
|
|
if (set->fb) {
|
|
meson_fb = to_am_meson_fb(set->fb);
|
|
/*logo init without waiting for VBlank.*/
|
|
meson_crtc_state->nonblock_by_vblank = true;
|
|
if (meson_fb->logo->vpp_index == VPP0) {
|
|
#ifdef CONFIG_AMLOGIC_VOUT_SERVE
|
|
meson_crtc_state->uboot_mode_init = get_vout_mode_uboot_state();
|
|
#endif
|
|
} else if (meson_fb->logo->vpp_index == VPP1) {
|
|
#ifdef CONFIG_AMLOGIC_VOUT2_SERVE
|
|
meson_crtc_state->uboot_mode_init = get_vout2_mode_uboot_state();
|
|
#endif
|
|
} else if (meson_fb->logo->vpp_index == VPP2) {
|
|
#ifdef CONFIG_AMLOGIC_VOUT3_SERVE
|
|
meson_crtc_state->uboot_mode_init = get_vout3_mode_uboot_state();
|
|
#endif
|
|
}
|
|
DRM_DEBUG("uboot_mode_init=%d\n", meson_crtc_state->uboot_mode_init);
|
|
}
|
|
|
|
osd_plane = private->osd_planes[idx];
|
|
if (!osd_plane)
|
|
return -EINVAL;
|
|
|
|
plane_state = drm_atomic_get_plane_state(state, &osd_plane->base);
|
|
if (IS_ERR(plane_state))
|
|
return PTR_ERR(plane_state);
|
|
|
|
if (!set->mode) {
|
|
WARN_ON(set->fb);
|
|
WARN_ON(set->num_connectors);
|
|
|
|
ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
crtc_state->active = false;
|
|
|
|
ret = drm_atomic_set_crtc_for_plane(plane_state, NULL);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
drm_atomic_set_fb_for_plane(plane_state, NULL);
|
|
|
|
goto commit;
|
|
}
|
|
|
|
if (WARN_ON(!set->fb))
|
|
return -EINVAL;
|
|
|
|
WARN_ON(!set->num_connectors);
|
|
|
|
ret = drm_atomic_set_mode_for_crtc(crtc_state, set->mode);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
crtc_state->active = true;
|
|
|
|
ret = drm_atomic_set_crtc_for_plane(plane_state, crtc);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
drm_mode_get_hv_timing(set->mode, &hdisplay, &vdisplay);
|
|
drm_atomic_set_fb_for_plane(plane_state, set->fb);
|
|
plane_state->crtc_x = 0;
|
|
plane_state->crtc_y = 0;
|
|
plane_state->crtc_w = hdisplay;
|
|
plane_state->crtc_h = vdisplay;
|
|
plane_state->src_x = set->x << 16;
|
|
plane_state->src_y = set->y << 16;
|
|
plane_state->zpos = zpos + osd_plane->plane_index;
|
|
|
|
switch (meson_fb->logo->osd_reverse) {
|
|
case 1:
|
|
plane_state->rotation = DRM_MODE_REFLECT_MASK;
|
|
break;
|
|
case 2:
|
|
plane_state->rotation = DRM_MODE_REFLECT_X;
|
|
break;
|
|
case 3:
|
|
plane_state->rotation = DRM_MODE_REFLECT_Y;
|
|
break;
|
|
default:
|
|
plane_state->rotation = DRM_MODE_ROTATE_0;
|
|
break;
|
|
}
|
|
|
|
if (drm_rotation_90_or_270(plane_state->rotation)) {
|
|
if (private->ui_config.ui_h)
|
|
plane_state->src_w = private->ui_config.ui_h << 16;
|
|
else
|
|
plane_state->src_w = set->fb->height << 16;
|
|
if (private->ui_config.ui_w)
|
|
plane_state->src_h = private->ui_config.ui_w << 16;
|
|
else
|
|
plane_state->src_h = set->fb->width << 16;
|
|
} else {
|
|
if (private->ui_config.ui_w)
|
|
plane_state->src_w = private->ui_config.ui_w << 16;
|
|
else
|
|
plane_state->src_w = set->fb->width << 16;
|
|
if (private->ui_config.ui_h)
|
|
plane_state->src_h = private->ui_config.ui_h << 16;
|
|
else
|
|
plane_state->src_h = set->fb->height << 16;
|
|
}
|
|
|
|
if (meson_fb->logo->vpp_index == VPP0)
|
|
_am_meson_occupy_plane_config(state, set);
|
|
|
|
commit:
|
|
ret = am_meson_update_output_state(state, set);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void am_meson_load_logo(struct drm_device *dev,
|
|
struct drm_framebuffer *fb, struct drm_atomic_state *state,
|
|
struct meson_vpu_sub_pipeline *sub_pipe)
|
|
{
|
|
struct drm_display_mode *mode;
|
|
struct drm_connector **connector_set;
|
|
struct drm_connector *connector;
|
|
struct meson_drm *private = dev->dev_private;
|
|
struct am_meson_fb *meson_fb;
|
|
char *connector_type = NULL;
|
|
u32 found, num_modes;
|
|
int ret = 0;
|
|
int idx = sub_pipe->index;
|
|
|
|
DRM_DEBUG("idx[%d]\n", idx);
|
|
|
|
if (!logo[VPP0].alloc_flag) {
|
|
DRM_INFO("%s: logo memory is not alloc\n", __func__);
|
|
return;
|
|
}
|
|
|
|
if (am_meson_logo_init_fb(dev, fb, idx)) {
|
|
DRM_DEBUG("vout%d logo is disabled!\n", idx + 1);
|
|
meson_fb = to_am_meson_fb(fb);
|
|
sub_pipe->logo_fb = meson_fb;
|
|
return;
|
|
}
|
|
|
|
switch (idx) {
|
|
case 0:
|
|
connector_type = get_uboot_connector0_type();
|
|
DRM_DEBUG("[%d]: connector_type %s\n", idx, connector_type);
|
|
break;
|
|
#ifdef CONFIG_AMLOGIC_VOUT2_SERVE
|
|
case 1:
|
|
connector_type = get_uboot_connector1_type();
|
|
DRM_DEBUG("[%d]: connector_type %s\n", idx, connector_type);
|
|
break;
|
|
#endif
|
|
#ifdef CONFIG_AMLOGIC_VOUT3_SERVE
|
|
case 2:
|
|
connector_type = get_uboot_connector2_type();
|
|
DRM_DEBUG("[%d]: connector_type %s\n", idx, connector_type);
|
|
break;
|
|
#endif
|
|
default:
|
|
DRM_DEBUG("connector_type not found\n");
|
|
break;
|
|
}
|
|
|
|
if (connector_type) {
|
|
if (strlen(connector_type) == 0 || strstr(connector_type, "NULL"))
|
|
connector_type = NULL;
|
|
}
|
|
|
|
meson_fb = to_am_meson_fb(fb);
|
|
/*init all connector and found matched uboot mode.*/
|
|
found = 0;
|
|
|
|
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
|
/*connector->name is same as connector_type char*/
|
|
if (connector_type && strcmp(connector->name, connector_type)) {
|
|
DRM_DEBUG("[%d]: %s != %s, jump\n", idx,
|
|
connector->name, connector_type);
|
|
continue;
|
|
}
|
|
|
|
if (drm_modeset_is_locked(&dev->mode_config.connection_mutex))
|
|
drm_modeset_unlock(&dev->mode_config.connection_mutex);
|
|
num_modes = connector->funcs->fill_modes(connector,
|
|
dev->mode_config.max_width,
|
|
dev->mode_config.max_height);
|
|
if (!drm_modeset_is_locked(&dev->mode_config.connection_mutex))
|
|
ret = drm_modeset_lock(&dev->mode_config.connection_mutex,
|
|
state->acquire_ctx);
|
|
|
|
if (ret)
|
|
DRM_ERROR("enter, drm_modeset_lock ret %d.\n", ret);
|
|
|
|
if (num_modes) {
|
|
list_for_each_entry(mode, &connector->modes, head) {
|
|
if (!strcmp(mode->name, meson_fb->logo->outputmode)) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (found)
|
|
break;
|
|
}
|
|
|
|
DRM_DEBUG("Connector[%d] status[%d]\n",
|
|
connector->connector_type, connector->status);
|
|
}
|
|
|
|
if (found) {
|
|
DRM_INFO("Found Connector[%d] mode[%s]\n",
|
|
connector->connector_type, mode->name);
|
|
if (!strcmp("null", mode->name)) {
|
|
DRM_INFO("NULL MODE, nothing to do.");
|
|
sub_pipe->logo_fb = meson_fb;
|
|
return;
|
|
}
|
|
} else {
|
|
connector = NULL;
|
|
mode = NULL;
|
|
sub_pipe->logo_fb = meson_fb;
|
|
return;
|
|
}
|
|
|
|
DRM_DEBUG("mode flag %x\n", mode->flags);
|
|
|
|
private->logo_set[idx].num_connectors = NUM_CONNECTORS_LOGO;
|
|
|
|
connector_set = kmalloc_array(private->logo_set[idx].num_connectors,
|
|
sizeof(struct drm_connector *), GFP_KERNEL);
|
|
if (!connector_set)
|
|
return;
|
|
|
|
connector_set[0] = connector;
|
|
private->logo_set[idx].connectors = connector_set;
|
|
|
|
private->logo_set[idx].crtc = &private->crtcs[idx]->base;
|
|
private->logo_set[idx].x = 0;
|
|
private->logo_set[idx].y = 0;
|
|
private->logo_set[idx].mode = mode;
|
|
private->logo_set[idx].crtc->mode = *mode;
|
|
private->logo_set[idx].fb = fb;
|
|
|
|
if (am_meson_drm_set_config(&private->logo_set[idx],
|
|
state, meson_fb->logo->panel_index))
|
|
DRM_INFO("[%s]am_meson_drm_set_config fail\n", __func__);
|
|
|
|
kfree(connector_set);
|
|
}
|
|
|
|
static int parse_reserve_mem_resource(struct device_node *np,
|
|
struct resource *res)
|
|
{
|
|
int ret;
|
|
|
|
ret = of_address_to_resource(np, 0, res);
|
|
of_node_put(np);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void am_meson_logo_cma_alloc(struct device *dev, int logo_init)
|
|
{
|
|
int ret;
|
|
#ifdef CONFIG_CMA
|
|
struct reserved_mem *rmem = NULL;
|
|
struct device_node *np, *mem_node;
|
|
#endif
|
|
struct meson_drm *private = dev_get_drvdata(dev);
|
|
|
|
np = gp_dev->dev.of_node;
|
|
mem_node = of_parse_phandle(np, "memory-region", 0);
|
|
|
|
ret = of_reserved_mem_device_init(&gp_dev->dev);
|
|
if (ret != 0) {
|
|
DRM_ERROR("failed to init reserved memory\n");
|
|
} else {
|
|
#ifdef CONFIG_CMA
|
|
rmem = of_reserved_mem_lookup(mem_node);
|
|
of_node_put(mem_node);
|
|
if (rmem) {
|
|
logo[VPP0].size = rmem->size;
|
|
DRM_DEBUG("of read %s reservememsize=0x%x, base %pa\n",
|
|
rmem->name, logo[VPP0].size, &rmem->base);
|
|
}
|
|
|
|
cma_logo = dev_get_cma_area(&gp_dev->dev);
|
|
|
|
if (cma_logo) {
|
|
if (logo[VPP0].size > 0) {
|
|
logo[VPP0].logo_page = cma_alloc(cma_logo,
|
|
ALIGN(logo[VPP0].size, PAGE_SIZE) >> PAGE_SHIFT,
|
|
0, GFP_KERNEL);
|
|
|
|
if (!logo[VPP0].logo_page)
|
|
DRM_ERROR("allocate buffer failed\n");
|
|
else if (logo_init)
|
|
am_meson_logo_info_update(private);
|
|
|
|
DRM_INFO(" cma_alloc from %s start page %px-%px size %x\n",
|
|
cma_get_name(cma_logo),
|
|
logo[VPP0].logo_page,
|
|
(void *)logo[VPP0].start,
|
|
logo[VPP0].size);
|
|
}
|
|
}
|
|
#endif
|
|
if (gem_mem_start && logo_init) {
|
|
dma_declare_coherent_memory(dev,
|
|
gem_mem_start,
|
|
gem_mem_start,
|
|
gem_mem_size);
|
|
DRM_INFO("meson drm mem_start = 0x%x, size = 0x%x\n",
|
|
(u32)gem_mem_start, (u32)gem_mem_size);
|
|
}
|
|
}
|
|
}
|
|
|
|
void am_meson_logo_cma_mem_reset_zero(struct am_meson_logo *logo)
|
|
{
|
|
phys_addr_t logo_addr;
|
|
void *vir_addr;
|
|
bool bflg = false;
|
|
|
|
if (logo->logo_page) {
|
|
logo_addr = page_to_phys(logo->logo_page);
|
|
vir_addr = am_meson_drm_vmap(logo_addr,
|
|
logo->size, &bflg);
|
|
memset(vir_addr, 0, logo->size);
|
|
logo->alloc_flag = 1;
|
|
} else {
|
|
DRM_ERROR("reallocate logo cma buffer failed\n");
|
|
}
|
|
}
|
|
|
|
void am_meson_logo_init(struct drm_device *dev)
|
|
{
|
|
struct drm_mode_fb_cmd2 mode_cmd;
|
|
struct drm_framebuffer *fb;
|
|
struct meson_drm *private = dev->dev_private;
|
|
struct meson_vpu_sub_pipeline *sub_pipe;
|
|
struct platform_device *pdev = to_platform_device(private->dev);
|
|
struct drm_atomic_state *state;
|
|
#ifdef CONFIG_CMA
|
|
struct device_node *np, *mem_node;
|
|
#endif
|
|
u32 reverse_type, osd_index;
|
|
int i, ret;
|
|
const char *compatible;
|
|
const int *reusable;
|
|
|
|
DRM_DEBUG("in[%d]\n", __LINE__);
|
|
|
|
gp_dev = pdev;
|
|
np = gp_dev->dev.of_node;
|
|
mem_node = of_parse_phandle(np, "memory-region", 0);
|
|
compatible = of_get_property(mem_node, "compatible", NULL);
|
|
reusable = of_get_property(mem_node, "reusable", NULL);
|
|
|
|
if (!strcmp(compatible, "shared-dma-pool") && reusable)
|
|
is_cma = true;
|
|
else
|
|
is_cma = false;
|
|
|
|
if (!mem_node || !of_device_is_available(mem_node)) {
|
|
DRM_INFO("mem region is disabled, skip allocation!\n");
|
|
} else if (is_cma) {
|
|
am_meson_logo_cma_alloc(dev->dev, 1);
|
|
} else {
|
|
ret = parse_reserve_mem_resource(mem_node, &osd_mem_res);
|
|
|
|
if (ret != 0) {
|
|
DRM_ERROR("failed to init none_cma memory\n");
|
|
} else {
|
|
logo[VPP0].size = resource_size(&osd_mem_res);
|
|
logo[VPP0].start = osd_mem_res.start;
|
|
}
|
|
|
|
if (logo[VPP0].size == 0) {
|
|
DRM_ERROR("logo size 0, error!\n");
|
|
} else {
|
|
logo[VPP0].vaddr = memremap(logo[VPP0].start, logo[VPP0].size,
|
|
MEMREMAP_WB);
|
|
|
|
if (!logo[VPP0].vaddr)
|
|
DRM_ERROR("allocate buffer failed\n");
|
|
else
|
|
am_meson_logo_info_update(private);
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef CONFIG_AMLOGIC_MEDIA_FB
|
|
get_logo_osd_reverse(&osd_index, &reverse_type);
|
|
logo[VPP0].osd_reverse = reverse_type;
|
|
logo[VPP0].width = get_logo_fb_width();
|
|
logo[VPP0].height = get_logo_fb_height();
|
|
logo[VPP0].bpp = get_logo_display_bpp();
|
|
#else
|
|
drm_logo_get_osd_reverse(&osd_index, &reverse_type);
|
|
logo[VPP0].osd_reverse = reverse_type;
|
|
logo[VPP0].width = drm_logo_get_fb_width();
|
|
logo[VPP0].height = drm_logo_get_fb_height();
|
|
logo[VPP0].bpp = drm_logo_get_display_bpp();
|
|
#endif
|
|
if (!logo[VPP0].bpp)
|
|
logo[VPP0].bpp = 16;
|
|
|
|
if (logo[VPP0].bpp == 16)
|
|
mode_cmd.pixel_format = DRM_FORMAT_RGB565;
|
|
else if (logo[VPP0].bpp == 24)
|
|
mode_cmd.pixel_format = DRM_FORMAT_RGB888;
|
|
else
|
|
mode_cmd.pixel_format = DRM_FORMAT_XRGB8888;
|
|
logo[VPP0].is_cma = is_cma;
|
|
|
|
mode_cmd.offsets[0] = 0;
|
|
mode_cmd.width = logo[VPP0].width;
|
|
mode_cmd.height = logo[VPP0].height;
|
|
mode_cmd.modifier[0] = DRM_FORMAT_MOD_LINEAR;
|
|
mode_cmd.pitches[0] = ALIGN(mode_cmd.width * logo[VPP0].bpp, 32) / 8;
|
|
fb = am_meson_fb_alloc(dev, &mode_cmd, NULL);
|
|
if (IS_ERR_OR_NULL(fb)) {
|
|
DRM_ERROR("drm fb allocate failed\n");
|
|
return;
|
|
}
|
|
|
|
if (strmode && !strcmp("4", strmode)) {
|
|
DRM_INFO("current is strmode\n");
|
|
} else {
|
|
drm_modeset_lock_all(dev);
|
|
state = drm_atomic_state_alloc(dev);
|
|
if (!state) {
|
|
drm_modeset_unlock_all(dev);
|
|
drm_framebuffer_put(fb);
|
|
DRM_ERROR("drm atomic state allocate failed\n");
|
|
return;
|
|
}
|
|
state->acquire_ctx = dev->mode_config.acquire_ctx;
|
|
|
|
for (i = 0; i < private->num_crtcs; i++) {
|
|
sub_pipe = private->pipeline->subs[i];
|
|
am_meson_load_logo(dev, fb, state, sub_pipe);
|
|
if (sub_pipe->logo_fb) {
|
|
drm_framebuffer_get(&sub_pipe->logo_fb->base);
|
|
DRM_INFO("logo_fb[id:%d,ref:%d]\n",
|
|
sub_pipe->logo_fb->base.base.id,
|
|
kref_read(&sub_pipe->logo_fb->base.base.refcount));
|
|
}
|
|
}
|
|
|
|
ret = drm_atomic_commit(state);
|
|
if (ret != 0) {
|
|
drm_atomic_state_put(state);
|
|
DRM_ERROR("failed to commit state\n");
|
|
drm_modeset_unlock_all(dev);
|
|
drm_framebuffer_put(fb);
|
|
return;
|
|
}
|
|
drm_atomic_state_put(state);
|
|
drm_modeset_unlock_all(dev);
|
|
}
|
|
|
|
if (drm_framebuffer_read_refcount(fb) > 1)
|
|
drm_framebuffer_put(fb);
|
|
|
|
DRM_DEBUG("drm_fb[id:%d,ref:%d]\n", fb->base.id, kref_read(&fb->base.refcount));
|
|
DRM_DEBUG("end[%d]\n", __LINE__);
|
|
}
|
|
|
|
static int gem_mem_device_init(struct reserved_mem *rmem, struct device *dev)
|
|
{
|
|
s32 ret = 0;
|
|
|
|
if (!rmem) {
|
|
pr_info("Can't get reverse mem!\n");
|
|
ret = -EFAULT;
|
|
return ret;
|
|
}
|
|
gem_mem_start = rmem->base;
|
|
gem_mem_size = rmem->size;
|
|
DRM_INFO("init gem memory source addr:0x%x size:0x%x\n",
|
|
(u32)gem_mem_start, (u32)gem_mem_size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct reserved_mem_ops rmem_gem_ops = {
|
|
.device_init = gem_mem_device_init,
|
|
};
|
|
|
|
static int __init gem_mem_setup(struct reserved_mem *rmem)
|
|
{
|
|
rmem->ops = &rmem_gem_ops;
|
|
DRM_INFO("gem mem setup\n");
|
|
return 0;
|
|
}
|
|
|
|
RESERVEDMEM_OF_DECLARE(gem, "amlogic, gem_memory", gem_mem_setup);
|
|
|