| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * Copyright (c) 2021 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_drv.h" |
| #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; |
| static struct platform_device *gp_dev; |
| static unsigned long gem_mem_start, gem_mem_size; |
| static struct resource osd_mem_res; |
| static bool is_cma; |
| |
| #ifdef MODULE |
| MODULE_PARM_DESC(outputmode, "outputmode"); |
| __module_param_named(outputmode, logo.outputmode_t, charp, 0600); |
| |
| #else |
| #ifndef CONFIG_AMLOGIC_MEDIA_FB |
| core_param(fb_width, logo.width, uint, 0644); |
| core_param(fb_height, logo.height, uint, 0644); |
| core_param(display_bpp, logo.bpp, uint, 0644); |
| #endif |
| core_param(outputmode, logo.outputmode_t, charp, 0644); |
| #endif |
| |
| #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); |
| return ret; |
| } |
| __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); |
| return ret; |
| } |
| __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); |
| return ret; |
| } |
| __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 0; |
| } |
| __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_AMLOGIC_MEMORY_EXTEND |
| #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); |
| } |
| } |
| } |
| #endif |
| |
| void am_meson_free_logo_memory(void) |
| { |
| if (is_cma) { |
| phys_addr_t logo_addr = page_to_phys(logo.logo_page); |
| |
| if (logo.size > 0 && logo.alloc_flag) { |
| #ifdef CONFIG_CMA |
| DRM_INFO("%s, free cma memory: addr:0x%pa,size:0x%x\n", |
| __func__, &logo_addr, logo.size); |
| |
| cma_release(cma_logo, logo.logo_page, logo.size >> PAGE_SHIFT); |
| #endif |
| } |
| } else { |
| #ifdef CONFIG_AMLOGIC_MEMORY_EXTEND |
| free_reserved_mem(logo.start, logo.size); |
| DRM_INFO("%s, free none_cma memory: addr:0x%pa,size:0x%x\n", |
| __func__, &logo.start, logo.size); |
| #endif |
| } |
| |
| logo.alloc_flag = 0; |
| } |
| |
| static int am_meson_logo_info_update(struct meson_drm *priv) |
| { |
| if (is_cma) |
| logo.start = page_to_phys(logo.logo_page); |
| |
| logo.alloc_flag = 1; |
| /*config 1080p logo as default*/ |
| if (!logo.width || !logo.height) { |
| logo.width = 1920; |
| logo.height = 1080; |
| } |
| if (!logo.bpp) |
| logo.bpp = 16; |
| if (!logo.outputmode_t) { |
| strcpy(logo.outputmode, "1080p60hz"); |
| } else { |
| strncpy(logo.outputmode, logo.outputmode_t, VMODE_NAME_LEN_MAX); |
| logo.outputmode[VMODE_NAME_LEN_MAX - 1] = '\0'; |
| } |
| priv->logo = &logo; |
| |
| 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; |
| struct meson_drm *priv = dev->dev_private; |
| |
| slogo = kzalloc(sizeof(*slogo), GFP_KERNEL); |
| if (!slogo) |
| return -EFAULT; |
| |
| memcpy(slogo, &logo, sizeof(struct am_meson_logo)); |
| if (idx == VPP0) { |
| #ifdef CONFIG_AMLOGIC_VOUT_SERVE |
| strcpy(slogo->outputmode, get_vout_mode_uboot()); |
| #endif |
| } else if (idx == VPP1) { |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| strcpy(slogo->outputmode, get_vout2_mode_uboot()); |
| #endif |
| } else if (idx == VPP2) { |
| #ifdef CONFIG_AMLOGIC_VOUT3_SERVE |
| strcpy(slogo->outputmode, get_vout3_mode_uboot()); |
| #endif |
| } |
| |
| if (!strcmp("null", slogo->outputmode) || |
| !strcmp("dummy_l", slogo->outputmode)) { |
| DRM_DEBUG("NULL MODE or DUMMY MODE, nothing to do."); |
| kfree(slogo); |
| return -EINVAL; |
| } |
| |
| slogo->logo_page = logo.logo_page; |
| slogo->vaddr = logo.vaddr; |
| slogo->start = logo.start; |
| slogo->panel_index = priv->primary_plane_index[idx]; |
| slogo->vpp_index = idx; |
| |
| DRM_INFO("logo%d width=%d,height=%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; |
| |
| 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_device *dev = set->crtc->dev; |
| 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; |
| |
| ret = drm_modeset_lock(&dev->mode_config.connection_mutex, |
| state->acquire_ctx); |
| if (ret) |
| return ret; |
| |
| /* 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; |
| } |
| |
| /*similar with __drm_atomic_helper_set_config, |
| *TODO:sync with __drm_atomic_helper_set_config |
| */ |
| 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; |
| 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_fb = to_am_meson_fb(set->fb); |
| meson_crtc_state = to_am_meson_crtc_state(crtc_state); |
| 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; |
| } |
| |
| WARN_ON(!set->fb); |
| 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 (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; |
| } |
| |
| /*copy from drm_atomic_helper_set_config, |
| *TODO:sync with drm_atomic_helper_set_config |
| */ |
| static int am_meson_drm_set_config(struct drm_mode_set *set, |
| struct drm_modeset_acquire_ctx *ctx, int idx) |
| { |
| struct drm_atomic_state *state; |
| struct drm_crtc *crtc = set->crtc; |
| int ret = 0; |
| |
| state = drm_atomic_state_alloc(crtc->dev); |
| if (!state) |
| return -ENOMEM; |
| |
| state->acquire_ctx = ctx; |
| ret = __am_meson_drm_set_config(set, state, idx); |
| if (ret != 0) |
| goto fail; |
| |
| ret = drm_atomic_commit(state); |
| |
| fail: |
| drm_atomic_state_put(state); |
| return ret; |
| } |
| |
| static void am_meson_load_logo(struct drm_device *dev, |
| struct drm_framebuffer *fb, int idx) |
| { |
| struct drm_mode_set set; |
| struct drm_display_mode *mode; |
| struct drm_connector **connector_set; |
| struct drm_connector *connector; |
| struct drm_modeset_acquire_ctx *ctx; |
| struct meson_drm *private = dev->dev_private; |
| struct am_meson_fb *meson_fb; |
| u32 found, num_modes; |
| |
| DRM_DEBUG("%s idx[%d]\n", __func__, idx); |
| |
| if (!logo.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); |
| return; |
| } |
| |
| 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) { |
| drm_modeset_lock_all(dev); |
| 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); |
| drm_modeset_unlock_all(dev); |
| |
| 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."); |
| return; |
| } |
| } else { |
| connector = NULL; |
| mode = NULL; |
| return; |
| } |
| |
| connector_set = vmalloc(sizeof(struct drm_connector *)); |
| if (!connector_set) |
| return; |
| |
| DRM_DEBUG("mode flag %x\n", mode->flags); |
| |
| connector_set[0] = connector; |
| set.crtc = &private->crtcs[idx]->base; |
| set.x = 0; |
| set.y = 0; |
| set.mode = mode; |
| set.crtc->mode = *mode; |
| set.connectors = connector_set; |
| set.num_connectors = 1; |
| set.fb = &meson_fb->base; |
| |
| drm_modeset_lock_all(dev); |
| ctx = dev->mode_config.acquire_ctx; |
| if (am_meson_drm_set_config(&set, ctx, meson_fb->logo->panel_index)) |
| DRM_INFO("[%s]am_meson_drm_set_config fail\n", __func__); |
| drm_modeset_unlock_all(dev); |
| |
| vfree(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_init(struct drm_device *dev) |
| { |
| struct drm_mode_fb_cmd2 mode_cmd; |
| struct drm_framebuffer *fb; |
| struct meson_drm *private = dev->dev_private; |
| struct platform_device *pdev = to_platform_device(private->dev); |
| #ifdef CONFIG_CMA |
| struct reserved_mem *rmem = NULL; |
| struct device_node *np, *mem_node; |
| #endif |
| u32 reverse_type, osd_index; |
| int i, ret; |
| const char *compatible; |
| const int *reusable; |
| |
| DRM_DEBUG("%s in[%d]\n", __func__, __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 (is_cma) { |
| DRM_INFO("allocate cmd mem\n"); |
| ret = of_reserved_mem_device_init(&gp_dev->dev); |
| if (ret != 0) { |
| DRM_ERROR("failed to init reserved memory\n"); |
| } else { |
| #ifdef CONFIG_CMA |
| if (mem_node) { |
| rmem = of_reserved_mem_lookup(mem_node); |
| of_node_put(mem_node); |
| if (rmem) { |
| logo.size = rmem->size; |
| DRM_DEBUG("of read %s reservememsize=0x%x, base %pa\n", |
| rmem->name, logo.size, &rmem->base); |
| } |
| } else { |
| DRM_ERROR("no memory-region\n"); |
| } |
| |
| cma_logo = dev_get_cma_area(&gp_dev->dev); |
| |
| if (cma_logo) { |
| if (logo.size > 0) { |
| #if CONFIG_AMLOGIC_KERNEL_VERSION >= 14515 |
| logo.logo_page = cma_alloc(cma_logo, |
| ALIGN(logo.size, PAGE_SIZE) >> PAGE_SHIFT, |
| 0, GFP_KERNEL); |
| #else |
| logo.logo_page = cma_alloc(cma_logo, |
| ALIGN(logo.size, PAGE_SIZE) >> PAGE_SHIFT, |
| 0, false); |
| #endif |
| if (!logo.logo_page) |
| DRM_ERROR("allocate buffer failed\n"); |
| else |
| 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.logo_page, |
| (void *)logo.start, |
| logo.size); |
| } |
| } |
| #endif |
| if (gem_mem_start) { |
| dma_declare_coherent_memory(dev->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); |
| } |
| } |
| } else { |
| DRM_INFO("allocate reserved mem\n"); |
| if (mem_node) { |
| ret = parse_reserve_mem_resource(mem_node, &osd_mem_res); |
| |
| if (ret != 0) { |
| DRM_ERROR("failed to init none_cma memory\n"); |
| } else { |
| logo.size = resource_size(&osd_mem_res); |
| logo.start = osd_mem_res.start; |
| DRM_INFO("of read reservememsize=0x%x--0x%x\n", |
| logo.size, (u32)logo.start); |
| } |
| |
| if (logo.size > 0) { |
| logo.vaddr = memremap(logo.start, logo.size, |
| MEMREMAP_WB); |
| |
| if (!logo.vaddr) |
| DRM_ERROR("allocate buffer failed\n"); |
| else |
| am_meson_logo_info_update(private); |
| } |
| |
| } else { |
| DRM_ERROR("no memory-region\n"); |
| } |
| } |
| |
| #ifdef CONFIG_AMLOGIC_MEDIA_FB |
| get_logo_osd_reverse(&osd_index, &reverse_type); |
| logo.osd_reverse = reverse_type; |
| logo.width = get_logo_fb_width(); |
| logo.height = get_logo_fb_height(); |
| logo.bpp = get_logo_display_bpp(); |
| #else |
| drm_logo_get_osd_reverse(&osd_index, &reverse_type); |
| logo.osd_reverse = reverse_type; |
| logo.width = drm_logo_get_fb_width(); |
| logo.height = drm_logo_get_fb_height(); |
| logo.bpp = drm_logo_get_display_bpp(); |
| #endif |
| if (!logo.bpp) |
| logo.bpp = 16; |
| |
| if (logo.bpp == 16) |
| mode_cmd.pixel_format = DRM_FORMAT_RGB565; |
| else if (logo.bpp == 24) |
| mode_cmd.pixel_format = DRM_FORMAT_RGB888; |
| else |
| mode_cmd.pixel_format = DRM_FORMAT_XRGB8888; |
| |
| mode_cmd.offsets[0] = 0; |
| mode_cmd.width = logo.width; |
| mode_cmd.height = logo.height; |
| mode_cmd.modifier[0] = DRM_FORMAT_MOD_LINEAR; |
| /*ToDo*/ |
| mode_cmd.pitches[0] = ALIGN(mode_cmd.width * logo.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"); |
| private->logo_show_done = true; |
| return; |
| } |
| |
| /*Todo: the condition may need change according to the boot args*/ |
| if (strmode && !strcmp("4", strmode)) |
| DRM_INFO("current is strmode\n"); |
| else |
| #ifndef CONFIG_AMLOGIC_ZAPPER_CUT |
| for (i = 0; i < private->num_crtcs; i++) |
| #endif |
| am_meson_load_logo(dev, fb, i); |
| |
| 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)); |
| |
| private->logo_show_done = true; |
| |
| DRM_DEBUG("%s end[%d]\n", __func__, __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); |
| |