From 9d77db3c730780c8ef5ccd4b66403ff5675cfe4e Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 13 May 2024 10:30:14 +0000 Subject: [PATCH] modify sin led gpio --- kernel/drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 2321 ++++++++++++++++++++++++++--------------------------------- 1 files changed, 1,020 insertions(+), 1,301 deletions(-) diff --git a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index 0ae26f7..9f64ad8 100644 --- a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -1,62 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd * Author:Mark Yao <mark.yao@rock-chips.com> * * based on exynos_drm_drv.c - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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_atomic.h> -#include <drm/drm_crtc_helper.h> -#include <drm/drm_fb_helper.h> -#include <drm/drm_gem_cma_helper.h> -#include <drm/drm_of.h> -#include <linux/devfreq.h> -#include <linux/dma-buf.h> +#include <linux/dma-buf-cache.h> #include <linux/dma-mapping.h> #include <linux/dma-iommu.h> #include <linux/genalloc.h> #include <linux/pm_runtime.h> -#include <linux/memblock.h> #include <linux/module.h> #include <linux/of_address.h> #include <linux/of_graph.h> +#include <linux/of_platform.h> #include <linux/clk.h> -#include <linux/clk-provider.h> #include <linux/component.h> #include <linux/console.h> #include <linux/iommu.h> #include <linux/of_reserved_mem.h> +#include <drm/drm_debugfs.h> +#include <drm/drm_drv.h> +#include <drm/drm_displayid.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_of.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_vblank.h> + #include "rockchip_drm_drv.h" #include "rockchip_drm_fb.h" #include "rockchip_drm_fbdev.h" #include "rockchip_drm_gem.h" +#include "rockchip_drm_logo.h" -#include "../drm_internal.h" +#include "../drm_crtc_internal.h" #define DRIVER_NAME "rockchip" #define DRIVER_DESC "RockChip Soc DRM" #define DRIVER_DATE "20140818" -#define DRIVER_MAJOR 2 +#define DRIVER_MAJOR 3 #define DRIVER_MINOR 0 -#define DRIVER_PATCH 0 - -/*********************************************************************** - * Rockchip DRM driver version - * - * v2.0.0 : add basic version for linux 4.19 rockchip drm driver(hjc) - * - **********************************************************************/ #if IS_ENABLED(CONFIG_DRM_ROCKCHIP_VVOP) static bool is_support_iommu = false; @@ -64,40 +50,234 @@ static bool is_support_iommu = true; #endif static bool iommu_reserve_map; + static struct drm_driver rockchip_drm_driver; -struct rockchip_drm_mode_set { - struct list_head head; - struct drm_framebuffer *fb; - struct drm_connector *connector; - struct drm_crtc *crtc; - struct drm_display_mode *mode; - int clock; - int hdisplay; - int vdisplay; - int vrefresh; - int flags; - int picture_aspect_ratio; - int crtc_hsync_end; - int crtc_vsync_end; +static unsigned int drm_debug; +module_param_named(debug, drm_debug, int, 0600); - int left_margin; - int right_margin; - int top_margin; - int bottom_margin; +static inline bool rockchip_drm_debug_enabled(enum rockchip_drm_debug_category category) +{ + return unlikely(drm_debug & category); +} - unsigned int brightness; - unsigned int contrast; - unsigned int saturation; - unsigned int hue; +__printf(3, 4) +void rockchip_drm_dbg(const struct device *dev, enum rockchip_drm_debug_category category, + const char *format, ...) +{ + struct va_format vaf; + va_list args; - bool mode_changed; - bool force_output; - int ratio; -}; + if (!rockchip_drm_debug_enabled(category)) + return; + + va_start(args, format); + vaf.fmt = format; + vaf.va = &args; + + if (dev) + dev_printk(KERN_DEBUG, dev, "%pV", &vaf); + else + printk(KERN_DEBUG "%pV", &vaf); + + va_end(args); +} + +/** + * rockchip_drm_wait_vact_end + * @crtc: CRTC to enable line flag + * @mstimeout: millisecond for timeout + * + * Wait for vact_end line flag irq or timeout. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout) +{ + struct rockchip_drm_private *priv; + int pipe, ret = 0; + + if (!crtc) + return -ENODEV; + + if (mstimeout <= 0) + return -EINVAL; + + priv = crtc->dev->dev_private; + pipe = drm_crtc_index(crtc); + + if (priv->crtc_funcs[pipe] && priv->crtc_funcs[pipe]->wait_vact_end) + ret = priv->crtc_funcs[pipe]->wait_vact_end(crtc, mstimeout); + + return ret; +} +EXPORT_SYMBOL(rockchip_drm_wait_vact_end); + +void drm_mode_convert_to_split_mode(struct drm_display_mode *mode) +{ + u16 hactive, hfp, hsync, hbp; + + hactive = mode->hdisplay; + hfp = mode->hsync_start - mode->hdisplay; + hsync = mode->hsync_end - mode->hsync_start; + hbp = mode->htotal - mode->hsync_end; + + mode->clock *= 2; + mode->crtc_clock *= 2; + mode->hdisplay = hactive * 2; + mode->hsync_start = mode->hdisplay + hfp * 2; + mode->hsync_end = mode->hsync_start + hsync * 2; + mode->htotal = mode->hsync_end + hbp * 2; + drm_mode_set_name(mode); +} +EXPORT_SYMBOL(drm_mode_convert_to_split_mode); + +void drm_mode_convert_to_origin_mode(struct drm_display_mode *mode) +{ + u16 hactive, hfp, hsync, hbp; + + hactive = mode->hdisplay; + hfp = mode->hsync_start - mode->hdisplay; + hsync = mode->hsync_end - mode->hsync_start; + hbp = mode->htotal - mode->hsync_end; + + mode->clock /= 2; + mode->crtc_clock /= 2; + mode->hdisplay = hactive / 2; + mode->hsync_start = mode->hdisplay + hfp / 2; + mode->hsync_end = mode->hsync_start + hsync / 2; + mode->htotal = mode->hsync_end + hbp / 2; +} +EXPORT_SYMBOL(drm_mode_convert_to_origin_mode); + +/** + * drm_connector_oob_hotplug_event - Report out-of-band hotplug event to connector + * @connector: connector to report the event on + * + * On some hardware a hotplug event notification may come from outside the display + * driver / device. An example of this is some USB Type-C setups where the hardware + * muxes the DisplayPort data and aux-lines but does not pass the altmode HPD + * status bit to the GPU's DP HPD pin. + * + * This function can be used to report these out-of-band events after obtaining + * a drm_connector reference through calling drm_connector_find_by_fwnode(). + */ +void drm_connector_oob_hotplug_event(struct fwnode_handle *connector_fwnode) +{ + struct rockchip_drm_sub_dev *sub_dev; + + if (!connector_fwnode || !connector_fwnode->dev) + return; + + sub_dev = rockchip_drm_get_sub_dev(dev_of_node(connector_fwnode->dev)); + + if (sub_dev && sub_dev->connector && sub_dev->oob_hotplug_event) + sub_dev->oob_hotplug_event(sub_dev->connector); +} +EXPORT_SYMBOL(drm_connector_oob_hotplug_event); + +uint32_t rockchip_drm_get_bpp(const struct drm_format_info *info) +{ + /* use whatever a driver has set */ + if (info->cpp[0]) + return info->cpp[0] * 8; + + switch (info->format) { + case DRM_FORMAT_YUV420_8BIT: + return 12; + case DRM_FORMAT_YUV420_10BIT: + return 15; + case DRM_FORMAT_VUY101010: + return 30; + default: + break; + } + + /* all attempts failed */ + return 0; +} +EXPORT_SYMBOL(rockchip_drm_get_bpp); + +uint32_t rockchip_drm_get_cycles_per_pixel(uint32_t bus_format) +{ + switch (bus_format) { + case MEDIA_BUS_FMT_RGB565_1X16: + case MEDIA_BUS_FMT_RGB666_1X18: + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: + return 1; + case MEDIA_BUS_FMT_RGB565_2X8_LE: + case MEDIA_BUS_FMT_BGR565_2X8_LE: + return 2; + case MEDIA_BUS_FMT_RGB666_3X6: + case MEDIA_BUS_FMT_RGB888_3X8: + case MEDIA_BUS_FMT_BGR888_3X8: + return 3; + case MEDIA_BUS_FMT_RGB888_DUMMY_4X8: + case MEDIA_BUS_FMT_BGR888_DUMMY_4X8: + return 4; + default: + return 1; + } +} +EXPORT_SYMBOL(rockchip_drm_get_cycles_per_pixel); + +/** + * rockchip_drm_of_find_possible_crtcs - find the possible CRTCs for an active + * encoder port + * @dev: DRM device + * @port: encoder port to scan for endpoints + * + * Scan all active endpoints attached to a port, locate their attached CRTCs, + * and generate the DRM mask of CRTCs which may be attached to this + * encoder. + * + * See Documentation/devicetree/bindings/graph.txt for the bindings. + */ +uint32_t rockchip_drm_of_find_possible_crtcs(struct drm_device *dev, + struct device_node *port) +{ + struct device_node *remote_port, *ep; + uint32_t possible_crtcs = 0; + + for_each_endpoint_of_node(port, ep) { + if (!of_device_is_available(ep)) + continue; + + remote_port = of_graph_get_remote_port(ep); + if (!remote_port) { + of_node_put(ep); + continue; + } + + possible_crtcs |= drm_of_crtc_port_mask(dev, remote_port); + + of_node_put(remote_port); + } + + return possible_crtcs; +} +EXPORT_SYMBOL(rockchip_drm_of_find_possible_crtcs); static DEFINE_MUTEX(rockchip_drm_sub_dev_lock); static LIST_HEAD(rockchip_drm_sub_dev_list); + +void rockchip_connector_update_vfp_for_vrr(struct drm_crtc *crtc, struct drm_display_mode *mode, + int vfp) +{ + struct rockchip_drm_sub_dev *sub_dev; + + mutex_lock(&rockchip_drm_sub_dev_lock); + list_for_each_entry(sub_dev, &rockchip_drm_sub_dev_list, list) { + if (sub_dev->connector->state->crtc == crtc) { + if (sub_dev->update_vfp_for_vrr) + sub_dev->update_vfp_for_vrr(sub_dev->connector, mode, vfp); + } + } + mutex_unlock(&rockchip_drm_sub_dev_lock); +} +EXPORT_SYMBOL(rockchip_connector_update_vfp_for_vrr); void rockchip_drm_register_sub_dev(struct rockchip_drm_sub_dev *sub_dev) { @@ -151,6 +331,26 @@ } EXPORT_SYMBOL(rockchip_drm_get_sub_dev_type); +u32 rockchip_drm_get_scan_line_time_ns(void) +{ + struct rockchip_drm_sub_dev *sub_dev = NULL; + struct drm_display_mode *mode; + int linedur_ns = 0; + + mutex_lock(&rockchip_drm_sub_dev_lock); + list_for_each_entry(sub_dev, &rockchip_drm_sub_dev_list, list) { + if (sub_dev->connector->encoder && sub_dev->connector->state->crtc) { + mode = &sub_dev->connector->state->crtc->state->adjusted_mode; + linedur_ns = div_u64((u64) mode->crtc_htotal * 1000000, mode->crtc_clock); + break; + } + } + mutex_unlock(&rockchip_drm_sub_dev_lock); + + return linedur_ns; +} +EXPORT_SYMBOL(rockchip_drm_get_scan_line_time_ns); + void rockchip_drm_te_handle(struct drm_crtc *crtc) { struct rockchip_drm_private *priv = crtc->dev->dev_private; @@ -166,22 +366,22 @@ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 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, }, + .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, /* 16 - 1920x1080@60Hz 16:9 */ { DRM_MODE("1920x1080", 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, }, + .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, /* 31 - 1920x1080@50Hz 16:9 */ { DRM_MODE("1920x1080", 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, }, + .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, /* 19 - 1280x720@50Hz 16:9 */ { DRM_MODE("1280x720", 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, }, + .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, /* 0x10 - 1024x768@60Hz */ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, 1184, 1344, 0, 768, 771, 777, 806, 0, @@ -190,12 +390,12 @@ { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, 796, 864, 0, 576, 581, 586, 625, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, + .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, /* 2 - 720x480@60Hz 4:3 */ { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736, 798, 858, 0, 480, 489, 495, 525, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, + .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, }; int rockchip_drm_add_modes_noedid(struct drm_connector *connector) @@ -224,1013 +424,638 @@ } EXPORT_SYMBOL(rockchip_drm_add_modes_noedid); -#ifdef CONFIG_ARCH_ROCKCHIP -struct drm_prime_callback_data { - struct drm_gem_object *obj; - struct sg_table *sgt; +static const struct rockchip_drm_width_dclk { + int width; + u32 dclk_khz; +} rockchip_drm_dclk[] = { + {1920, 148500}, + {2048, 200000}, + {2560, 280000}, + {3840, 594000}, + {4096, 594000}, + {7680, 2376000}, }; -#endif -#ifndef MODULE -static struct drm_crtc *find_crtc_by_node(struct drm_device *drm_dev, struct device_node *node) +u32 rockchip_drm_get_dclk_by_width(int width) { - struct device_node *np_crtc; - struct drm_crtc *crtc; + int i = 0; + u32 dclk_khz; - np_crtc = of_get_parent(node); - if (!np_crtc || !of_device_is_available(np_crtc)) + for (i = 0; i < ARRAY_SIZE(rockchip_drm_dclk); i++) { + if (width == rockchip_drm_dclk[i].width) { + dclk_khz = rockchip_drm_dclk[i].dclk_khz; + break; + } + } + + if (i == ARRAY_SIZE(rockchip_drm_dclk)) { + DRM_ERROR("Can't not find %d width solution and use 148500 khz as max dclk\n", width); + + dclk_khz = 148500; + } + + return dclk_khz; +} +EXPORT_SYMBOL(rockchip_drm_get_dclk_by_width); + +static int +cea_db_tag(const u8 *db) +{ + return db[0] >> 5; +} + +static int +cea_db_payload_len(const u8 *db) +{ + return db[0] & 0x1f; +} + +#define for_each_cea_db(cea, i, start, end) \ + for ((i) = (start); \ + (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); \ + (i) += cea_db_payload_len(&(cea)[(i)]) + 1) + +#define HDMI_NEXT_HDR_VSDB_OUI 0xd04601 + +static bool cea_db_is_hdmi_next_hdr_block(const u8 *db) +{ + unsigned int oui; + + if (cea_db_tag(db) != 0x07) + return false; + + if (cea_db_payload_len(db) < 11) + return false; + + oui = db[3] << 16 | db[2] << 8 | db[1]; + + return oui == HDMI_NEXT_HDR_VSDB_OUI; +} + +static bool cea_db_is_hdmi_forum_vsdb(const u8 *db) +{ + unsigned int oui; + + if (cea_db_tag(db) != 0x03) + return false; + + if (cea_db_payload_len(db) < 7) + return false; + + oui = db[3] << 16 | db[2] << 8 | db[1]; + + return oui == HDMI_FORUM_IEEE_OUI; +} + +static int +cea_db_offsets(const u8 *cea, int *start, int *end) +{ + /* DisplayID CTA extension blocks and top-level CEA EDID + * block header definitions differ in the following bytes: + * 1) Byte 2 of the header specifies length differently, + * 2) Byte 3 is only present in the CEA top level block. + * + * The different definitions for byte 2 follow. + * + * DisplayID CTA extension block defines byte 2 as: + * Number of payload bytes + * + * CEA EDID block defines byte 2 as: + * Byte number (decimal) within this block where the 18-byte + * DTDs begin. If no non-DTD data is present in this extension + * block, the value should be set to 04h (the byte after next). + * If set to 00h, there are no DTDs present in this block and + * no non-DTD data. + */ + if (cea[0] == 0x81) { + /* + * for_each_displayid_db() has already verified + * that these stay within expected bounds. + */ + *start = 3; + *end = *start + cea[2]; + } else if (cea[0] == 0x02) { + /* Data block offset in CEA extension block */ + *start = 4; + *end = cea[2]; + if (*end == 0) + *end = 127; + if (*end < 4 || *end > 127) + return -ERANGE; + } else { + return -EOPNOTSUPP; + } + + return 0; +} + +static u8 *find_edid_extension(const struct edid *edid, + int ext_id, int *ext_index) +{ + u8 *edid_ext = NULL; + int i; + + /* No EDID or EDID extensions */ + if (edid == NULL || edid->extensions == 0) return NULL; - drm_for_each_crtc(crtc, drm_dev) { - if (crtc->port == np_crtc) - return crtc; + /* Find CEA extension */ + for (i = *ext_index; i < edid->extensions; i++) { + edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1); + if (edid_ext[0] == ext_id) + break; + } + + if (i >= edid->extensions) + return NULL; + + *ext_index = i + 1; + + return edid_ext; +} + +static int validate_displayid(u8 *displayid, int length, int idx) +{ + int i, dispid_length; + u8 csum = 0; + struct displayid_hdr *base; + + base = (struct displayid_hdr *)&displayid[idx]; + + DRM_DEBUG_KMS("base revision 0x%x, length %d, %d %d\n", + base->rev, base->bytes, base->prod_id, base->ext_count); + + /* +1 for DispID checksum */ + dispid_length = sizeof(*base) + base->bytes + 1; + if (dispid_length > length - idx) + return -EINVAL; + + for (i = 0; i < dispid_length; i++) + csum += displayid[idx + i]; + if (csum) { + DRM_NOTE("DisplayID checksum invalid, remainder is %d\n", csum); + return -EINVAL; + } + + return 0; +} + +static u8 *find_displayid_extension(const struct edid *edid, + int *length, int *idx, + int *ext_index) +{ + u8 *displayid = find_edid_extension(edid, 0x70, ext_index); + struct displayid_hdr *base; + int ret; + + if (!displayid) + return NULL; + + /* EDID extensions block checksum isn't for us */ + *length = EDID_LENGTH - 1; + *idx = 1; + + ret = validate_displayid(displayid, *length, *idx); + if (ret) + return NULL; + + base = (struct displayid_hdr *)&displayid[*idx]; + *length = *idx + sizeof(*base) + base->bytes; + + return displayid; +} + +static u8 *find_cea_extension(const struct edid *edid) +{ + int length, idx; + struct displayid_block *block; + u8 *cea; + u8 *displayid; + int ext_index; + + /* Look for a top level CEA extension block */ + /* FIXME: make callers iterate through multiple CEA ext blocks? */ + ext_index = 0; + cea = find_edid_extension(edid, 0x02, &ext_index); + if (cea) + return cea; + + /* CEA blocks can also be found embedded in a DisplayID block */ + ext_index = 0; + for (;;) { + displayid = find_displayid_extension(edid, &length, &idx, + &ext_index); + if (!displayid) + return NULL; + + idx += sizeof(struct displayid_hdr); + for_each_displayid_db(displayid, block, idx, length) { + if (block->tag == 0x81) + return (u8 *)block; + } } return NULL; } -static struct drm_connector *find_connector_by_node(struct drm_device *drm_dev, - struct device_node *node) +#define EDID_CEA_YCRCB422 (1 << 4) + +int rockchip_drm_get_yuv422_format(struct drm_connector *connector, + struct edid *edid) { - struct device_node *np_connector; - struct rockchip_drm_sub_dev *sub_dev; + struct drm_display_info *info; + const u8 *edid_ext; - np_connector = of_graph_get_remote_port_parent(node); - if (!np_connector || !of_device_is_available(np_connector)) - return NULL; + if (!connector || !edid) + return -EINVAL; - sub_dev = rockchip_drm_get_sub_dev(np_connector); - if (!sub_dev) - return NULL; + info = &connector->display_info; - return sub_dev->connector; -} + edid_ext = find_cea_extension(edid); + if (!edid_ext) + return -EINVAL; -static struct drm_connector *find_connector_by_bridge(struct drm_device *drm_dev, - struct device_node *node) -{ - struct device_node *np_encoder, *np_connector = NULL; - struct drm_connector *connector = NULL; - struct device_node *port, *endpoint; - struct rockchip_drm_sub_dev *sub_dev; - - np_encoder = of_graph_get_remote_port_parent(node); - if (!np_encoder || !of_device_is_available(np_encoder)) - goto err_put_encoder; - - port = of_graph_get_port_by_id(np_encoder, 1); - if (!port) { - dev_err(drm_dev->dev, "can't found port point!\n"); - goto err_put_encoder; - } - - for_each_child_of_node(port, endpoint) { - np_connector = of_graph_get_remote_port_parent(endpoint); - if (!np_connector) { - dev_err(drm_dev->dev, - "can't found connector node, please init!\n"); - goto err_put_port; - } - if (!of_device_is_available(np_connector)) { - of_node_put(np_connector); - np_connector = NULL; - continue; - } else { - break; - } - } - if (!np_connector) { - dev_err(drm_dev->dev, "can't found available connector node!\n"); - goto err_put_port; - } - - sub_dev = rockchip_drm_get_sub_dev(np_connector); - if (!sub_dev) - goto err_put_port; - connector = sub_dev->connector; - - of_node_put(np_connector); -err_put_port: - of_node_put(port); -err_put_encoder: - of_node_put(np_encoder); - - return connector; -} - -void rockchip_free_loader_memory(struct drm_device *drm) -{ - struct rockchip_drm_private *private = drm->dev_private; - struct rockchip_logo *logo; - void *start, *end; - - if (!private || !private->logo || --private->logo->count) - return; - - logo = private->logo; - start = phys_to_virt(logo->dma_addr); - end = phys_to_virt(logo->dma_addr + logo->size); - - if (private->domain) { - u32 pg_size = 1UL << __ffs(private->domain->pgsize_bitmap); - - iommu_unmap(private->domain, logo->dma_addr, ALIGN(logo->size, pg_size)); - } - - memblock_free(logo->start, logo->size); - free_reserved_area(start, end, -1, "drm_logo"); - kfree(logo); - private->logo = NULL; - private->loader_protect = false; -} - -static int init_loader_memory(struct drm_device *drm_dev) -{ - struct rockchip_drm_private *private = drm_dev->dev_private; - struct rockchip_logo *logo; - struct device_node *np = drm_dev->dev->of_node; - struct device_node *node; - phys_addr_t start, size; - u32 pg_size = PAGE_SIZE; - struct resource res; - int ret, idx; - - idx = of_property_match_string(np, "memory-region-names", "drm-logo"); - if (idx >= 0) - node = of_parse_phandle(np, "memory-region", idx); - else - node = of_parse_phandle(np, "logo-memory-region", 0); - if (!node) - return -ENOMEM; - - ret = of_address_to_resource(node, 0, &res); - if (ret) - return ret; - if (private->domain) - pg_size = 1UL << __ffs(private->domain->pgsize_bitmap); - start = ALIGN_DOWN(res.start, pg_size); - size = resource_size(&res); - if (!size) - return -ENOMEM; - - logo = kmalloc(sizeof(*logo), GFP_KERNEL); - if (!logo) - return -ENOMEM; - - logo->kvaddr = phys_to_virt(start); - - if (private->domain) { - ret = iommu_map(private->domain, start, start, ALIGN(size, pg_size), - IOMMU_WRITE | IOMMU_READ); - if (ret) { - dev_err(drm_dev->dev, "failed to create 1v1 mapping\n"); - goto err_free_logo; - } - } - - logo->dma_addr = start; - logo->size = size; - logo->count = 1; - private->logo = logo; - - idx = of_property_match_string(np, "memory-region-names", "drm-cubic-lut"); - if (idx < 0) - return 0; - - node = of_parse_phandle(np, "memory-region", idx); - if (!node) - return -ENOMEM; - - ret = of_address_to_resource(node, 0, &res); - if (ret) - return ret; - start = ALIGN_DOWN(res.start, pg_size); - size = resource_size(&res); - if (!size) - return 0; - - private->cubic_lut_kvaddr = phys_to_virt(start); - if (private->domain) { - ret = iommu_map(private->domain, start, start, ALIGN(size, pg_size), - IOMMU_WRITE | IOMMU_READ); - if (ret) { - dev_err(drm_dev->dev, "failed to create 1v1 mapping for cubic lut\n"); - goto err_free_logo; - } - } - private->cubic_lut_dma_addr = start; + if (edid_ext[3] & EDID_CEA_YCRCB422) + info->color_formats |= DRM_COLOR_FORMAT_YCRCB422; return 0; - -err_free_logo: - kfree(logo); - - return ret; } +EXPORT_SYMBOL(rockchip_drm_get_yuv422_format); -static struct drm_framebuffer * -get_framebuffer_by_node(struct drm_device *drm_dev, struct device_node *node) +static +void get_max_frl_rate(int max_frl_rate, u8 *max_lanes, u8 *max_rate_per_lane) { - struct rockchip_drm_private *private = drm_dev->dev_private; - struct drm_mode_fb_cmd2 mode_cmd = { 0 }; - u32 val; - int bpp; - - if (WARN_ON(!private->logo)) - return NULL; - - if (of_property_read_u32(node, "logo,offset", &val)) { - pr_err("%s: failed to get logo,offset\n", __func__); - return NULL; - } - mode_cmd.offsets[0] = val; - - if (of_property_read_u32(node, "logo,width", &val)) { - pr_err("%s: failed to get logo,width\n", __func__); - return NULL; - } - mode_cmd.width = val; - - if (of_property_read_u32(node, "logo,height", &val)) { - pr_err("%s: failed to get logo,height\n", __func__); - return NULL; - } - mode_cmd.height = val; - - if (of_property_read_u32(node, "logo,bpp", &val)) { - pr_err("%s: failed to get logo,bpp\n", __func__); - return NULL; - } - bpp = val; - - mode_cmd.pitches[0] = ALIGN(mode_cmd.width * bpp, 32) / 8; - - switch (bpp) { - case 16: - mode_cmd.pixel_format = DRM_FORMAT_RGB565; + switch (max_frl_rate) { + case 1: + *max_lanes = 3; + *max_rate_per_lane = 3; break; - case 24: - mode_cmd.pixel_format = DRM_FORMAT_RGB888; + case 2: + *max_lanes = 3; + *max_rate_per_lane = 6; break; - case 32: - mode_cmd.pixel_format = DRM_FORMAT_XRGB8888; + case 3: + *max_lanes = 4; + *max_rate_per_lane = 6; break; + case 4: + *max_lanes = 4; + *max_rate_per_lane = 8; + break; + case 5: + *max_lanes = 4; + *max_rate_per_lane = 10; + break; + case 6: + *max_lanes = 4; + *max_rate_per_lane = 12; + break; + case 0: default: - pr_err("%s: unsupported to logo bpp %d\n", __func__, bpp); - return NULL; + *max_lanes = 0; + *max_rate_per_lane = 0; } - - return rockchip_fb_alloc(drm_dev, &mode_cmd, NULL, private->logo, 1); } -static struct rockchip_drm_mode_set * -of_parse_display_resource(struct drm_device *drm_dev, struct device_node *route) +#define EDID_DSC_10BPC (1 << 0) +#define EDID_DSC_12BPC (1 << 1) +#define EDID_DSC_16BPC (1 << 2) +#define EDID_DSC_ALL_BPP (1 << 3) +#define EDID_DSC_NATIVE_420 (1 << 6) +#define EDID_DSC_1P2 (1 << 7) +#define EDID_DSC_MAX_FRL_RATE_MASK 0xf0 +#define EDID_DSC_MAX_SLICES 0xf +#define EDID_DSC_TOTAL_CHUNK_KBYTES 0x3f +#define EDID_MAX_FRL_RATE_MASK 0xf0 + +static +void parse_edid_forum_vsdb(struct rockchip_drm_dsc_cap *dsc_cap, + u8 *max_frl_rate_per_lane, u8 *max_lanes, u8 *add_func, + const u8 *hf_vsdb) { - struct rockchip_drm_private *private = drm_dev->dev_private; - struct rockchip_drm_mode_set *set; - struct device_node *connect; - struct drm_framebuffer *fb; - struct drm_connector *connector; - struct drm_crtc *crtc; - const char *string; - u32 val; + u8 max_frl_rate; + u8 dsc_max_frl_rate; + u8 dsc_max_slices; - connect = of_parse_phandle(route, "connect", 0); - if (!connect) - return NULL; - - fb = get_framebuffer_by_node(drm_dev, route); - if (IS_ERR_OR_NULL(fb)) - return NULL; - - crtc = find_crtc_by_node(drm_dev, connect); - connector = find_connector_by_node(drm_dev, connect); - if (!connector) - connector = find_connector_by_bridge(drm_dev, connect); - if (!crtc || !connector) { - dev_warn(drm_dev->dev, - "No available crtc or connector for display"); - drm_framebuffer_put(fb); - return NULL; - } - - set = kzalloc(sizeof(*set), GFP_KERNEL); - if (!set) - return NULL; - - if (!of_property_read_u32(route, "video,clock", &val)) - set->clock = val; - - if (!of_property_read_u32(route, "video,hdisplay", &val)) - set->hdisplay = val; - - if (!of_property_read_u32(route, "video,vdisplay", &val)) - set->vdisplay = val; - - if (!of_property_read_u32(route, "video,crtc_hsync_end", &val)) - set->crtc_hsync_end = val; - - if (!of_property_read_u32(route, "video,crtc_vsync_end", &val)) - set->crtc_vsync_end = val; - - if (!of_property_read_u32(route, "video,vrefresh", &val)) - set->vrefresh = val; - - if (!of_property_read_u32(route, "video,flags", &val)) - set->flags = val; - - if (!of_property_read_u32(route, "video,aspect_ratio", &val)) - set->picture_aspect_ratio = val; - - if (!of_property_read_u32(route, "overscan,left_margin", &val)) - set->left_margin = val; - - if (!of_property_read_u32(route, "overscan,right_margin", &val)) - set->right_margin = val; - - if (!of_property_read_u32(route, "overscan,top_margin", &val)) - set->top_margin = val; - - if (!of_property_read_u32(route, "overscan,bottom_margin", &val)) - set->bottom_margin = val; - - if (!of_property_read_u32(route, "bcsh,brightness", &val)) - set->brightness = val; - else - set->brightness = 50; - - if (!of_property_read_u32(route, "bcsh,contrast", &val)) - set->contrast = val; - else - set->contrast = 50; - - if (!of_property_read_u32(route, "bcsh,saturation", &val)) - set->saturation = val; - else - set->saturation = 50; - - if (!of_property_read_u32(route, "bcsh,hue", &val)) - set->hue = val; - else - set->hue = 50; - - set->force_output = of_property_read_bool(route, "force-output"); - - if (!of_property_read_u32(route, "cubic_lut,offset", &val)) { - private->cubic_lut[crtc->index].enable = true; - private->cubic_lut[crtc->index].offset = val; - } - - set->ratio = 1; - if (!of_property_read_string(route, "logo,mode", &string) && - !strcmp(string, "fullscreen")) - set->ratio = 0; - - set->fb = fb; - set->crtc = crtc; - set->connector = connector; - - return set; -} - -static int rockchip_drm_fill_connector_modes(struct drm_connector *connector, - uint32_t maxX, uint32_t maxY, - bool force_output) -{ - struct drm_device *dev = connector->dev; - struct drm_display_mode *mode; - const struct drm_connector_helper_funcs *connector_funcs = - connector->helper_private; - int count = 0; - bool verbose_prune = true; - enum drm_connector_status old_status; - - WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); - - DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, - connector->name); - /* set all modes to the unverified state */ - list_for_each_entry(mode, &connector->modes, head) - mode->status = MODE_STALE; - - if (force_output) - connector->force = DRM_FORCE_ON; - if (connector->force) { - if (connector->force == DRM_FORCE_ON || - connector->force == DRM_FORCE_ON_DIGITAL) - connector->status = connector_status_connected; - else - connector->status = connector_status_disconnected; - if (connector->funcs->force) - connector->funcs->force(connector); - } else { - old_status = connector->status; - - if (connector->funcs->detect) - connector->status = connector->funcs->detect(connector, true); - else - connector->status = connector_status_connected; - /* - * Normally either the driver's hpd code or the poll loop should - * pick up any changes and fire the hotplug event. But if - * userspace sneaks in a probe, we might miss a change. Hence - * check here, and if anything changed start the hotplug code. - */ - if (old_status != connector->status) { - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %d to %d\n", - connector->base.id, - connector->name, - old_status, connector->status); - - /* - * The hotplug event code might call into the fb - * helpers, and so expects that we do not hold any - * locks. Fire up the poll struct instead, it will - * disable itself again. - */ - dev->mode_config.delayed_event = true; - if (dev->mode_config.poll_enabled) - schedule_delayed_work(&dev->mode_config.output_poll_work, - 0); - } - } - - /* Re-enable polling in case the global poll config changed. */ - if (!dev->mode_config.poll_running) - drm_kms_helper_poll_enable(dev); - - dev->mode_config.poll_running = true; - - if (connector->status == connector_status_disconnected) { - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] disconnected\n", - connector->base.id, connector->name); - drm_connector_update_edid_property(connector, NULL); - verbose_prune = false; - goto prune; - } - - count = (*connector_funcs->get_modes)(connector); - - if (count == 0 && connector->status == connector_status_connected) - count = drm_add_modes_noedid(connector, 1024, 768); - if (force_output) - count += rockchip_drm_add_modes_noedid(connector); - if (count == 0) - goto prune; - - drm_connector_list_update(connector); - - list_for_each_entry(mode, &connector->modes, head) { - if (mode->status == MODE_OK) - mode->status = drm_mode_validate_driver(dev, mode); - - if (mode->status == MODE_OK) - mode->status = drm_mode_validate_size(mode, maxX, maxY); - - /** - * if (mode->status == MODE_OK) - * mode->status = drm_mode_validate_flag(mode, mode_flags); - */ - if (mode->status == MODE_OK && connector_funcs->mode_valid) - mode->status = connector_funcs->mode_valid(connector, - mode); - if (mode->status == MODE_OK) - mode->status = drm_mode_validate_ycbcr420(mode, - connector); - } - -prune: - drm_mode_prune_invalid(dev, &connector->modes, verbose_prune); - - if (list_empty(&connector->modes)) - return 0; - - list_for_each_entry(mode, &connector->modes, head) - mode->vrefresh = drm_mode_vrefresh(mode); - - drm_mode_sort(&connector->modes); - - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] probed modes :\n", connector->base.id, - connector->name); - list_for_each_entry(mode, &connector->modes, head) { - drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); - drm_mode_debug_printmodeline(mode); - } - - return count; -} - -static int setup_initial_state(struct drm_device *drm_dev, - struct drm_atomic_state *state, - struct rockchip_drm_mode_set *set) -{ - struct rockchip_drm_private *priv = drm_dev->dev_private; - struct drm_connector *connector = set->connector; - struct drm_crtc *crtc = set->crtc; - struct drm_crtc_state *crtc_state; - struct drm_connector_state *conn_state; - struct drm_plane_state *primary_state; - struct drm_display_mode *mode = NULL; - const struct drm_connector_helper_funcs *funcs; - const struct drm_encoder_helper_funcs *encoder_funcs; - int pipe = drm_crtc_index(crtc); - bool is_crtc_enabled = true; - int hdisplay, vdisplay; - int fb_width, fb_height; - int found = 0, match = 0; - int num_modes; - int ret = 0; - struct rockchip_crtc_state *s = NULL; - - if (!set->hdisplay || !set->vdisplay || !set->vrefresh) - is_crtc_enabled = false; - - conn_state = drm_atomic_get_connector_state(state, connector); - if (IS_ERR(conn_state)) - return PTR_ERR(conn_state); - - funcs = connector->helper_private; - - if (funcs->best_encoder) - conn_state->best_encoder = funcs->best_encoder(connector); - else - conn_state->best_encoder = drm_atomic_helper_best_encoder(connector); - - if (funcs->loader_protect) - funcs->loader_protect(connector, true); - connector->loader_protect = true; - encoder_funcs = conn_state->best_encoder->helper_private; - if (encoder_funcs->loader_protect) - encoder_funcs->loader_protect(conn_state->best_encoder, true); - conn_state->best_encoder->loader_protect = true; - num_modes = rockchip_drm_fill_connector_modes(connector, 4096, 4096, set->force_output); - if (!num_modes) { - dev_err(drm_dev->dev, "connector[%s] can't found any modes\n", - connector->name); - ret = -EINVAL; - goto error_conn; - } - - list_for_each_entry(mode, &connector->modes, head) { - if (mode->clock == set->clock && - mode->hdisplay == set->hdisplay && - mode->vdisplay == set->vdisplay && - mode->crtc_hsync_end == set->crtc_hsync_end && - mode->crtc_vsync_end == set->crtc_vsync_end && - drm_mode_vrefresh(mode) == set->vrefresh && - /* we just need to focus on DRM_MODE_FLAG_ALL flag, so here - * we compare mode->flags with set->flags & DRM_MODE_FLAG_ALL. - */ - mode->flags == (set->flags & DRM_MODE_FLAG_ALL) && - mode->picture_aspect_ratio == set->picture_aspect_ratio) { - found = 1; - match = 1; - break; - } - } - - if (!found) { - ret = -EINVAL; - connector->status = connector_status_disconnected; - goto error_conn; - } - - conn_state->tv.brightness = set->brightness; - conn_state->tv.contrast = set->contrast; - conn_state->tv.saturation = set->saturation; - conn_state->tv.hue = set->hue; - set->mode = mode; - crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(crtc_state)) { - ret = PTR_ERR(crtc_state); - goto error_conn; - } - - drm_mode_copy(&crtc_state->adjusted_mode, mode); - if (!match || !is_crtc_enabled) { - set->mode_changed = true; - } else { - ret = drm_atomic_set_crtc_for_connector(conn_state, crtc); - if (ret) - goto error_conn; - - mode->picture_aspect_ratio = HDMI_PICTURE_ASPECT_NONE; - ret = drm_atomic_set_mode_for_crtc(crtc_state, mode); - if (ret) - goto error_conn; - - crtc_state->active = true; - - if (priv->crtc_funcs[pipe] && - priv->crtc_funcs[pipe]->loader_protect) - priv->crtc_funcs[pipe]->loader_protect(crtc, true); - } - - if (!set->fb) { - ret = 0; - goto error_crtc; - } - primary_state = drm_atomic_get_plane_state(state, crtc->primary); - if (IS_ERR(primary_state)) { - ret = PTR_ERR(primary_state); - goto error_crtc; - } - - hdisplay = mode->hdisplay; - vdisplay = mode->vdisplay; - fb_width = set->fb->width; - fb_height = set->fb->height; - - primary_state->crtc = crtc; - primary_state->src_x = 0; - primary_state->src_y = 0; - primary_state->src_w = fb_width << 16; - primary_state->src_h = fb_height << 16; - if (set->ratio) { - if (set->fb->width >= hdisplay) { - primary_state->crtc_x = 0; - primary_state->crtc_w = hdisplay; - } else { - primary_state->crtc_x = (hdisplay - fb_width) / 2; - primary_state->crtc_w = set->fb->width; - } - - if (set->fb->height >= vdisplay) { - primary_state->crtc_y = 0; - primary_state->crtc_h = vdisplay; - } else { - primary_state->crtc_y = (vdisplay - fb_height) / 2; - primary_state->crtc_h = fb_height; - } - } else { - primary_state->crtc_x = 0; - primary_state->crtc_y = 0; - primary_state->crtc_w = hdisplay; - primary_state->crtc_h = vdisplay; - } - s = to_rockchip_crtc_state(crtc->state); - s->output_type = connector->connector_type; - - return 0; - -error_crtc: - if (priv->crtc_funcs[pipe] && priv->crtc_funcs[pipe]->loader_protect) - priv->crtc_funcs[pipe]->loader_protect(crtc, false); -error_conn: - if (funcs->loader_protect) - funcs->loader_protect(connector, false); - connector->loader_protect = false; - if (encoder_funcs->loader_protect) - encoder_funcs->loader_protect(conn_state->best_encoder, false); - conn_state->best_encoder->loader_protect = false; - - return ret; -} - -static int update_state(struct drm_device *drm_dev, - struct drm_atomic_state *state, - struct rockchip_drm_mode_set *set, - unsigned int *plane_mask) -{ - struct drm_crtc *crtc = set->crtc; - struct drm_connector *connector = set->connector; - struct drm_display_mode *mode = set->mode; - struct drm_plane_state *primary_state; - struct drm_crtc_state *crtc_state; - struct drm_connector_state *conn_state; - int ret; - struct rockchip_crtc_state *s; - - crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(crtc_state)) - return PTR_ERR(crtc_state); - conn_state = drm_atomic_get_connector_state(state, connector); - if (IS_ERR(conn_state)) - return PTR_ERR(conn_state); - s = to_rockchip_crtc_state(crtc_state); - s->left_margin = set->left_margin; - s->right_margin = set->right_margin; - s->top_margin = set->top_margin; - s->bottom_margin = set->bottom_margin; - - if (set->mode_changed) { - ret = drm_atomic_set_crtc_for_connector(conn_state, crtc); - if (ret) - return ret; - - ret = drm_atomic_set_mode_for_crtc(crtc_state, mode); - if (ret) - return ret; - - crtc_state->active = true; - } else { - const struct drm_encoder_helper_funcs *encoder_helper_funcs; - const struct drm_connector_helper_funcs *connector_helper_funcs; - struct drm_encoder *encoder; - - connector_helper_funcs = connector->helper_private; - if (!connector_helper_funcs) - return -ENXIO; - if (connector_helper_funcs->best_encoder) - encoder = connector_helper_funcs->best_encoder(connector); - else - encoder = drm_atomic_helper_best_encoder(connector); - if (!encoder) - return -ENXIO; - encoder_helper_funcs = encoder->helper_private; - if (!encoder_helper_funcs->atomic_check) - return -ENXIO; - ret = encoder_helper_funcs->atomic_check(encoder, crtc->state, - conn_state); - if (ret) - return ret; - - if (encoder_helper_funcs->atomic_mode_set) - encoder_helper_funcs->atomic_mode_set(encoder, - crtc_state, - conn_state); - else if (encoder_helper_funcs->mode_set) - encoder_helper_funcs->mode_set(encoder, mode, mode); - } - - primary_state = drm_atomic_get_plane_state(state, crtc->primary); - if (IS_ERR(primary_state)) - return PTR_ERR(primary_state); - - crtc_state->plane_mask = 1 << drm_plane_index(crtc->primary); - *plane_mask |= crtc_state->plane_mask; - - drm_atomic_set_fb_for_plane(primary_state, set->fb); - drm_framebuffer_put(set->fb); - ret = drm_atomic_set_crtc_for_plane(primary_state, crtc); - - return ret; -} - -static void show_loader_logo(struct drm_device *drm_dev) -{ - struct drm_atomic_state *state, *old_state; - struct device_node *np = drm_dev->dev->of_node; - struct drm_mode_config *mode_config = &drm_dev->mode_config; - struct rockchip_drm_private *private = drm_dev->dev_private; - struct device_node *root, *route; - struct rockchip_drm_mode_set *set, *tmp, *unset; - struct list_head mode_set_list; - struct list_head mode_unset_list; - unsigned int plane_mask = 0; - int ret, i; - - root = of_get_child_by_name(np, "route"); - if (!root) { - dev_warn(drm_dev->dev, "failed to parse display resources\n"); + if (!hf_vsdb[7]) return; - } - if (init_loader_memory(drm_dev)) { - dev_warn(drm_dev->dev, "failed to parse loader memory\n"); + DRM_DEBUG_KMS("hdmi_21 sink detected. parsing edid\n"); + max_frl_rate = (hf_vsdb[7] & EDID_MAX_FRL_RATE_MASK) >> 4; + get_max_frl_rate(max_frl_rate, max_lanes, + max_frl_rate_per_lane); + + *add_func = hf_vsdb[8]; + + if (cea_db_payload_len(hf_vsdb) < 13) return; + + dsc_cap->v_1p2 = hf_vsdb[11] & EDID_DSC_1P2; + + if (!dsc_cap->v_1p2) + return; + + dsc_cap->native_420 = hf_vsdb[11] & EDID_DSC_NATIVE_420; + dsc_cap->all_bpp = hf_vsdb[11] & EDID_DSC_ALL_BPP; + + if (hf_vsdb[11] & EDID_DSC_16BPC) + dsc_cap->bpc_supported = 16; + else if (hf_vsdb[11] & EDID_DSC_12BPC) + dsc_cap->bpc_supported = 12; + else if (hf_vsdb[11] & EDID_DSC_10BPC) + dsc_cap->bpc_supported = 10; + else + dsc_cap->bpc_supported = 0; + + dsc_max_frl_rate = (hf_vsdb[12] & EDID_DSC_MAX_FRL_RATE_MASK) >> 4; + get_max_frl_rate(dsc_max_frl_rate, &dsc_cap->max_lanes, + &dsc_cap->max_frl_rate_per_lane); + dsc_cap->total_chunk_kbytes = hf_vsdb[13] & EDID_DSC_TOTAL_CHUNK_KBYTES; + + dsc_max_slices = hf_vsdb[12] & EDID_DSC_MAX_SLICES; + switch (dsc_max_slices) { + case 1: + dsc_cap->max_slices = 1; + dsc_cap->clk_per_slice = 340; + break; + case 2: + dsc_cap->max_slices = 2; + dsc_cap->clk_per_slice = 340; + break; + case 3: + dsc_cap->max_slices = 4; + dsc_cap->clk_per_slice = 340; + break; + case 4: + dsc_cap->max_slices = 8; + dsc_cap->clk_per_slice = 340; + break; + case 5: + dsc_cap->max_slices = 8; + dsc_cap->clk_per_slice = 400; + break; + case 6: + dsc_cap->max_slices = 12; + dsc_cap->clk_per_slice = 400; + break; + case 7: + dsc_cap->max_slices = 16; + dsc_cap->clk_per_slice = 400; + break; + case 0: + default: + dsc_cap->max_slices = 0; + dsc_cap->clk_per_slice = 0; } - - INIT_LIST_HEAD(&mode_set_list); - INIT_LIST_HEAD(&mode_unset_list); - drm_modeset_lock_all(drm_dev); - state = drm_atomic_state_alloc(drm_dev); - if (!state) { - dev_err(drm_dev->dev, "failed to alloc atomic state\n"); - ret = -ENOMEM; - goto err_unlock; - } - - state->acquire_ctx = mode_config->acquire_ctx; - - for_each_child_of_node(root, route) { - if (!of_device_is_available(route)) - continue; - - set = of_parse_display_resource(drm_dev, route); - if (!set) - continue; - - if (setup_initial_state(drm_dev, state, set)) { - drm_framebuffer_put(set->fb); - INIT_LIST_HEAD(&set->head); - list_add_tail(&set->head, &mode_unset_list); - continue; - } - INIT_LIST_HEAD(&set->head); - list_add_tail(&set->head, &mode_set_list); - } - - /* - * the mode_unset_list store the unconnected route, if route's crtc - * isn't used, we should close it. - */ - list_for_each_entry_safe(unset, tmp, &mode_unset_list, head) { - struct rockchip_drm_mode_set *tmp_set; - int find_used_crtc = 0; - - list_for_each_entry_safe(set, tmp_set, &mode_set_list, head) { - if (set->crtc == unset->crtc) { - find_used_crtc = 1; - continue; - } - } - - if (!find_used_crtc) { - struct drm_crtc *crtc = unset->crtc; - int pipe = drm_crtc_index(crtc); - struct rockchip_drm_private *priv = - drm_dev->dev_private; - - if (unset->hdisplay && unset->vdisplay) { - if (priv->crtc_funcs[pipe] && - priv->crtc_funcs[pipe]->loader_protect) - priv->crtc_funcs[pipe]->loader_protect(crtc, true); - priv->crtc_funcs[pipe]->crtc_close(crtc); - if (priv->crtc_funcs[pipe] && - priv->crtc_funcs[pipe]->loader_protect) - priv->crtc_funcs[pipe]->loader_protect(crtc, false); - } - } - - list_del(&unset->head); - kfree(unset); - } - - if (list_empty(&mode_set_list)) { - dev_warn(drm_dev->dev, "can't not find any loader display\n"); - ret = -ENXIO; - goto err_free_state; - } - - /* - * The state save initial devices status, swap the state into - * drm devices as old state, so if new state come, can compare - * with this state to judge which status need to update. - */ - WARN_ON(drm_atomic_helper_swap_state(state, false)); - drm_atomic_state_put(state); - old_state = drm_atomic_helper_duplicate_state(drm_dev, - mode_config->acquire_ctx); - if (IS_ERR(old_state)) { - dev_err(drm_dev->dev, "failed to duplicate atomic state\n"); - ret = PTR_ERR_OR_ZERO(old_state); - goto err_free_state; - } - - state = drm_atomic_helper_duplicate_state(drm_dev, - mode_config->acquire_ctx); - if (IS_ERR(state)) { - dev_err(drm_dev->dev, "failed to duplicate atomic state\n"); - ret = PTR_ERR_OR_ZERO(state); - goto err_free_old_state; - } - state->acquire_ctx = mode_config->acquire_ctx; - list_for_each_entry(set, &mode_set_list, head) - /* - * We don't want to see any fail on update_state. - */ - WARN_ON(update_state(drm_dev, state, set, &plane_mask)); - - for (i = 0; i < state->num_connector; i++) { - if (state->connectors[i].new_state->connector->status != - connector_status_connected) - state->connectors[i].new_state->best_encoder = NULL; - } - - ret = drm_atomic_commit(state); - /** - * todo - * drm_atomic_clean_old_fb(drm_dev, plane_mask, ret); - */ - - list_for_each_entry_safe(set, tmp, &mode_set_list, head) { - if (set->force_output) - set->connector->force = DRM_FORCE_UNSPECIFIED; - list_del(&set->head); - kfree(set); - } - - /* - * Is possible get deadlock here? - */ - WARN_ON(ret == -EDEADLK); - - if (ret) { - /* - * restore display status if atomic commit failed. - */ - WARN_ON(drm_atomic_helper_swap_state(old_state, false)); - goto err_free_state; - } - - rockchip_free_loader_memory(drm_dev); - drm_atomic_state_put(old_state); - drm_atomic_state_put(state); - - private->loader_protect = true; - drm_modeset_unlock_all(drm_dev); - return; -err_free_old_state: - drm_atomic_state_put(old_state); -err_free_state: - drm_atomic_state_put(state); -err_unlock: - drm_modeset_unlock_all(drm_dev); - if (ret) - dev_err(drm_dev->dev, "failed to show loader logo\n"); } -static const char *const loader_protect_clocks[] __initconst = { - "hclk_vio", - "hclk_vop", - "hclk_vopb", - "hclk_vopl", - "aclk_vio", - "aclk_vio0", - "aclk_vio1", - "aclk_vop", - "aclk_vopb", - "aclk_vopl", - "aclk_vo_pre", - "aclk_vio_pre", - "dclk_vop", - "dclk_vop0", - "dclk_vop1", - "dclk_vopb", - "dclk_vopl", +enum { + VER_26_BYTE_V0, + VER_15_BYTE_V1, + VER_12_BYTE_V1, + VER_12_BYTE_V2, }; -static struct clk **loader_clocks __initdata; -static int __init rockchip_clocks_loader_protect(void) +static int check_next_hdr_version(const u8 *next_hdr_db) { - int nclocks = ARRAY_SIZE(loader_protect_clocks); - struct clk *clk; - int i; + u16 ver; - loader_clocks = kcalloc(nclocks, sizeof(void *), GFP_KERNEL); - if (!loader_clocks) - return -ENOMEM; + ver = (next_hdr_db[5] & 0xf0) << 8 | next_hdr_db[0]; - for (i = 0; i < nclocks; i++) { - clk = __clk_lookup(loader_protect_clocks[i]); - - if (clk) { - loader_clocks[i] = clk; - clk_prepare_enable(clk); - } + switch (ver) { + case 0x00f9: + return VER_26_BYTE_V0; + case 0x20ee: + return VER_15_BYTE_V1; + case 0x20eb: + return VER_12_BYTE_V1; + case 0x40eb: + return VER_12_BYTE_V2; + default: + return -ENOENT; } - - return 0; } -arch_initcall_sync(rockchip_clocks_loader_protect); -static int __init rockchip_clocks_loader_unprotect(void) +static void parse_ver_26_v0_data(struct ver_26_v0 *hdr, const u8 *data) { - int i; + hdr->yuv422_12bit = data[5] & BIT(0); + hdr->support_2160p_60 = (data[5] & BIT(1)) >> 1; + hdr->global_dimming = (data[5] & BIT(2)) >> 2; - if (!loader_clocks) - return -ENODEV; + hdr->dm_major_ver = (data[21] & 0xf0) >> 4; + hdr->dm_minor_ver = data[21] & 0xf; - for (i = 0; i < ARRAY_SIZE(loader_protect_clocks); i++) { - struct clk *clk = loader_clocks[i]; + hdr->t_min_pq = (data[19] << 4) | ((data[18] & 0xf0) >> 4); + hdr->t_max_pq = (data[20] << 4) | (data[18] & 0xf); - if (clk) - clk_disable_unprepare(clk); + hdr->rx = (data[7] << 4) | ((data[6] & 0xf0) >> 4); + hdr->ry = (data[8] << 4) | (data[6] & 0xf); + hdr->gx = (data[10] << 4) | ((data[9] & 0xf0) >> 4); + hdr->gy = (data[11] << 4) | (data[9] & 0xf); + hdr->bx = (data[13] << 4) | ((data[12] & 0xf0) >> 4); + hdr->by = (data[14] << 4) | (data[12] & 0xf); + hdr->wx = (data[16] << 4) | ((data[15] & 0xf0) >> 4); + hdr->wy = (data[17] << 4) | (data[15] & 0xf); +} + +static void parse_ver_15_v1_data(struct ver_15_v1 *hdr, const u8 *data) +{ + hdr->yuv422_12bit = data[5] & BIT(0); + hdr->support_2160p_60 = (data[5] & BIT(1)) >> 1; + hdr->global_dimming = data[6] & BIT(0); + + hdr->dm_version = (data[5] & 0x1c) >> 2; + + hdr->colorimetry = data[7] & BIT(0); + + hdr->t_max_lum = (data[6] & 0xfe) >> 1; + hdr->t_min_lum = (data[7] & 0xfe) >> 1; + + hdr->rx = data[9]; + hdr->ry = data[10]; + hdr->gx = data[11]; + hdr->gy = data[12]; + hdr->bx = data[13]; + hdr->by = data[14]; +} + +static void parse_ver_12_v1_data(struct ver_12_v1 *hdr, const u8 *data) +{ + hdr->yuv422_12bit = data[5] & BIT(0); + hdr->support_2160p_60 = (data[5] & BIT(1)) >> 1; + hdr->global_dimming = data[6] & BIT(0); + + hdr->dm_version = (data[5] & 0x1c) >> 2; + + hdr->colorimetry = data[7] & BIT(0); + + hdr->t_max_lum = (data[6] & 0xfe) >> 1; + hdr->t_min_lum = (data[7] & 0xfe) >> 1; + + hdr->low_latency = data[8] & 0x3; + + hdr->unique_rx = (data[11] & 0xf8) >> 3; + hdr->unique_ry = (data[11] & 0x7) << 2 | (data[10] & BIT(0)) << 1 | + (data[9] & BIT(0)); + hdr->unique_gx = (data[9] & 0xfe) >> 1; + hdr->unique_gy = (data[10] & 0xfe) >> 1; + hdr->unique_bx = (data[8] & 0xe0) >> 5; + hdr->unique_by = (data[8] & 0x1c) >> 2; +} + +static void parse_ver_12_v2_data(struct ver_12_v2 *hdr, const u8 *data) +{ + hdr->yuv422_12bit = data[5] & BIT(0); + hdr->backlt_ctrl = (data[5] & BIT(1)) >> 1; + hdr->global_dimming = (data[6] & BIT(2)) >> 2; + + hdr->dm_version = (data[5] & 0x1c) >> 2; + hdr->backlt_min_luma = data[6] & 0x3; + hdr->interface = data[7] & 0x3; + hdr->yuv444_10b_12b = (data[8] & BIT(0)) << 1 | (data[9] & BIT(0)); + + hdr->t_min_pq_v2 = (data[6] & 0xf8) >> 3; + hdr->t_max_pq_v2 = (data[7] & 0xf8) >> 3; + + hdr->unique_rx = (data[10] & 0xf8) >> 3; + hdr->unique_ry = (data[11] & 0xf8) >> 3; + hdr->unique_gx = (data[8] & 0xfe) >> 1; + hdr->unique_gy = (data[9] & 0xfe) >> 1; + hdr->unique_bx = data[10] & 0x7; + hdr->unique_by = data[11] & 0x7; +} + +static +void parse_next_hdr_block(struct next_hdr_sink_data *sink_data, + const u8 *next_hdr_db) +{ + int version; + + version = check_next_hdr_version(next_hdr_db); + if (version < 0) + return; + + sink_data->version = version; + + switch (version) { + case VER_26_BYTE_V0: + parse_ver_26_v0_data(&sink_data->ver_26_v0, next_hdr_db); + break; + case VER_15_BYTE_V1: + parse_ver_15_v1_data(&sink_data->ver_15_v1, next_hdr_db); + break; + case VER_12_BYTE_V1: + parse_ver_12_v1_data(&sink_data->ver_12_v1, next_hdr_db); + break; + case VER_12_BYTE_V2: + parse_ver_12_v2_data(&sink_data->ver_12_v2, next_hdr_db); + break; + default: + break; } - kfree(loader_clocks); - - return 0; } -late_initcall_sync(rockchip_clocks_loader_unprotect); -#endif -int rockchip_drm_crtc_send_mcu_cmd(struct drm_device *drm_dev, - struct device_node *np_crtc, - u32 type, u32 value) +int rockchip_drm_parse_cea_ext(struct rockchip_drm_dsc_cap *dsc_cap, + u8 *max_frl_rate_per_lane, u8 *max_lanes, u8 *add_func, + const struct edid *edid) { - struct drm_crtc *crtc; - int pipe = 0; - struct rockchip_drm_private *priv; + const u8 *edid_ext; + int i, start, end; - if (!np_crtc || !of_device_is_available(np_crtc)) + if (!dsc_cap || !max_frl_rate_per_lane || !max_lanes || !edid || !add_func) return -EINVAL; - drm_for_each_crtc(crtc, drm_dev) { - if (of_get_parent(crtc->port) == np_crtc) - break; - } - - pipe = drm_crtc_index(crtc); - if (pipe >= ROCKCHIP_MAX_CRTC) + edid_ext = find_cea_extension(edid); + if (!edid_ext) return -EINVAL; - priv = crtc->dev->dev_private; - if (priv->crtc_funcs[pipe]->crtc_send_mcu_cmd) - priv->crtc_funcs[pipe]->crtc_send_mcu_cmd(crtc, type, value); + + if (cea_db_offsets(edid_ext, &start, &end)) + return -EINVAL; + + for_each_cea_db(edid_ext, i, start, end) { + const u8 *db = &edid_ext[i]; + + if (cea_db_is_hdmi_forum_vsdb(db)) + parse_edid_forum_vsdb(dsc_cap, max_frl_rate_per_lane, + max_lanes, add_func, db); + } return 0; } -EXPORT_SYMBOL(rockchip_drm_crtc_send_mcu_cmd); +EXPORT_SYMBOL(rockchip_drm_parse_cea_ext); + +int rockchip_drm_parse_next_hdr(struct next_hdr_sink_data *sink_data, + const struct edid *edid) +{ + const u8 *edid_ext; + int i, start, end; + + if (!sink_data || !edid) + return -EINVAL; + + memset(sink_data, 0, sizeof(struct next_hdr_sink_data)); + + edid_ext = find_cea_extension(edid); + if (!edid_ext) + return -EINVAL; + + if (cea_db_offsets(edid_ext, &start, &end)) + return -EINVAL; + + for_each_cea_db(edid_ext, i, start, end) { + const u8 *db = &edid_ext[i]; + + if (cea_db_is_hdmi_next_hdr_block(db)) + parse_next_hdr_block(sink_data, db); + } + + return 0; +} +EXPORT_SYMBOL(rockchip_drm_parse_next_hdr); + +#define COLORIMETRY_DATA_BLOCK 0x5 +#define USE_EXTENDED_TAG 0x07 + +static bool cea_db_is_hdmi_colorimetry_data_block(const u8 *db) +{ + if (cea_db_tag(db) != USE_EXTENDED_TAG) + return false; + + if (db[1] != COLORIMETRY_DATA_BLOCK) + return false; + + return true; +} + +int +rockchip_drm_parse_colorimetry_data_block(u8 *colorimetry, const struct edid *edid) +{ + const u8 *edid_ext; + int i, start, end; + + if (!colorimetry || !edid) + return -EINVAL; + + *colorimetry = 0; + + edid_ext = find_cea_extension(edid); + if (!edid_ext) + return -EINVAL; + + if (cea_db_offsets(edid_ext, &start, &end)) + return -EINVAL; + + for_each_cea_db(edid_ext, i, start, end) { + const u8 *db = &edid_ext[i]; + + if (cea_db_is_hdmi_colorimetry_data_block(db)) + /* As per CEA 861-G spec */ + *colorimetry = ((db[3] & (0x1 << 7)) << 1) | db[2]; + } + + return 0; +} +EXPORT_SYMBOL(rockchip_drm_parse_colorimetry_data_block); /* * Attach a (component) device to the shared drm dma mapping from master drm @@ -1265,6 +1090,17 @@ return; iommu_detach_device(domain, dev); +} + +void rockchip_drm_crtc_standby(struct drm_crtc *crtc, bool standby) +{ + struct rockchip_drm_private *priv = crtc->dev->dev_private; + int pipe = drm_crtc_index(crtc); + + if (pipe < ROCKCHIP_MAX_CRTC && + priv->crtc_funcs[pipe] && + priv->crtc_funcs[pipe]->crtc_standby) + priv->crtc_funcs[pipe]->crtc_standby(crtc, standby); } int rockchip_register_crtc_funcs(struct drm_crtc *crtc, @@ -1383,16 +1219,12 @@ struct drm_minor *minor = node->minor; struct drm_device *drm_dev = minor->dev; struct rockchip_drm_private *priv = drm_dev->dev_private; - struct drm_printer p = drm_seq_file_printer(s); if (!priv->domain) return 0; - mutex_lock(&priv->mm_lock); - drm_mm_print(&priv->mm, &p); - mutex_unlock(&priv->mm_lock); return 0; @@ -1462,21 +1294,15 @@ { "mm_dump", rockchip_drm_mm_dump, 0, NULL }, }; -static int rockchip_drm_debugfs_init(struct drm_minor *minor) +static void rockchip_drm_debugfs_init(struct drm_minor *minor) { struct drm_device *dev = minor->dev; struct rockchip_drm_private *priv = dev->dev_private; struct drm_crtc *crtc; - int ret; - ret = drm_debugfs_create_files(rockchip_debugfs_files, - ARRAY_SIZE(rockchip_debugfs_files), - minor->debugfs_root, - minor); - if (ret) { - dev_err(dev->dev, "could not install rockchip_debugfs_list\n"); - return ret; - } + drm_debugfs_create_files(rockchip_debugfs_files, + ARRAY_SIZE(rockchip_debugfs_files), + minor->debugfs_root, minor); drm_for_each_crtc(crtc, dev) { int pipe = drm_crtc_index(crtc); @@ -1485,10 +1311,14 @@ priv->crtc_funcs[pipe]->debugfs_init) priv->crtc_funcs[pipe]->debugfs_init(minor, crtc); } - - return 0; } #endif + +static const struct drm_prop_enum_list split_area[] = { + { ROCKCHIP_DRM_SPLIT_UNSET, "UNSET" }, + { ROCKCHIP_DRM_SPLIT_LEFT_SIDE, "LEFT" }, + { ROCKCHIP_DRM_SPLIT_RIGHT_SIDE, "RIGHT" }, +}; static int rockchip_drm_create_properties(struct drm_device *dev) { @@ -1508,24 +1338,6 @@ private->color_space_prop = prop; prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, - "GLOBAL_ALPHA", 0, 255); - if (!prop) - return -ENOMEM; - private->global_alpha_prop = prop; - - prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, - "BLEND_MODE", 0, 1); - if (!prop) - return -ENOMEM; - private->blend_mode_prop = prop; - - prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, - "ALPHA_SCALE", 0, 1); - if (!prop) - return -ENOMEM; - private->alpha_scale_prop = prop; - - prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, "ASYNC_COMMIT", 0, 1); if (!prop) return -ENOMEM; @@ -1537,53 +1349,35 @@ return -ENOMEM; private->share_id_prop = prop; - prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_IMMUTABLE, "CONNECTOR_ID", 0, 0xf); if (!prop) return -ENOMEM; private->connector_id_prop = prop; + prop = drm_property_create_enum(dev, DRM_MODE_PROP_ENUM, "SPLIT_AREA", + split_area, + ARRAY_SIZE(split_area)); + private->split_area_prop = prop; + + prop = drm_property_create_object(dev, + DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_IMMUTABLE, + "SOC_ID", DRM_MODE_OBJECT_CRTC); + private->soc_id_prop = prop; + + prop = drm_property_create_object(dev, + DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_IMMUTABLE, + "PORT_ID", DRM_MODE_OBJECT_CRTC); + private->port_id_prop = prop; + + private->aclk_prop = drm_property_create_range(dev, 0, "ACLK", 0, UINT_MAX); + private->bg_prop = drm_property_create_range(dev, 0, "BACKGROUND", 0, UINT_MAX); + private->line_flag_prop = drm_property_create_range(dev, 0, "LINE_FLAG1", 0, UINT_MAX); + private->cubic_lut_prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "CUBIC_LUT", 0); + private->cubic_lut_size_prop = drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, + "CUBIC_LUT_SIZE", 0, UINT_MAX); + return drm_mode_create_tv_properties(dev, 0, NULL); -} - -static int rockchip_gem_pool_init(struct drm_device *drm) -{ - struct rockchip_drm_private *private = drm->dev_private; - struct device_node *np = drm->dev->of_node; - struct device_node *node; - phys_addr_t start, size; - struct resource res; - int ret; - - node = of_parse_phandle(np, "secure-memory-region", 0); - if (!node) - return -ENXIO; - - ret = of_address_to_resource(node, 0, &res); - if (ret) - return ret; - start = res.start; - size = resource_size(&res); - if (!size) - return -ENOMEM; - - private->secure_buffer_pool = gen_pool_create(PAGE_SHIFT, -1); - if (!private->secure_buffer_pool) - return -ENOMEM; - - gen_pool_add(private->secure_buffer_pool, start, size, -1); - - return 0; -} - -static void rockchip_gem_pool_destroy(struct drm_device *drm) -{ - struct rockchip_drm_private *private = drm->dev_private; - - if (!private->secure_buffer_pool) - return; - - gen_pool_destroy(private->secure_buffer_pool); } static void rockchip_attach_connector_property(struct drm_device *drm) @@ -1655,20 +1449,44 @@ drm_modeset_unlock_all(drm); } -static bool is_support_hotplug(uint32_t output_type) +static int rockchip_gem_pool_init(struct drm_device *drm) { - switch (output_type) { - case DRM_MODE_CONNECTOR_DVII: - case DRM_MODE_CONNECTOR_DVID: - case DRM_MODE_CONNECTOR_DVIA: - case DRM_MODE_CONNECTOR_DisplayPort: - case DRM_MODE_CONNECTOR_HDMIA: - case DRM_MODE_CONNECTOR_HDMIB: - case DRM_MODE_CONNECTOR_TV: - return true; - default: - return false; - } + struct rockchip_drm_private *private = drm->dev_private; + struct device_node *np = drm->dev->of_node; + struct device_node *node; + phys_addr_t start, size; + struct resource res; + int ret; + + node = of_parse_phandle(np, "secure-memory-region", 0); + if (!node) + return -ENXIO; + + ret = of_address_to_resource(node, 0, &res); + if (ret) + return ret; + start = res.start; + size = resource_size(&res); + if (!size) + return -ENOMEM; + + private->secure_buffer_pool = gen_pool_create(PAGE_SHIFT, -1); + if (!private->secure_buffer_pool) + return -ENOMEM; + + gen_pool_add(private->secure_buffer_pool, start, size, -1); + + return 0; +} + +static void rockchip_gem_pool_destroy(struct drm_device *drm) +{ + struct rockchip_drm_private *private = drm->dev_private; + + if (!private->secure_buffer_pool) + return; + + gen_pool_destroy(private->secure_buffer_pool); } static int rockchip_drm_bind(struct device *dev) @@ -1676,9 +1494,6 @@ struct drm_device *drm_dev; struct rockchip_drm_private *private; int ret; - struct device_node *np = dev->of_node; - struct device_node *parent_np; - struct drm_crtc *crtc; drm_dev = drm_dev_alloc(&rockchip_drm_driver, dev); if (IS_ERR(drm_dev)) @@ -1692,35 +1507,16 @@ goto err_free; } - mutex_init(&private->commit_lock); mutex_init(&private->ovl_lock); - INIT_WORK(&private->commit_work, rockchip_drm_atomic_work); + drm_dev->dev_private = private; - private->dmc_support = false; - private->devfreq = devfreq_get_devfreq_by_phandle(dev, 0); - if (IS_ERR(private->devfreq)) { - if (PTR_ERR(private->devfreq) == -EPROBE_DEFER) { - parent_np = of_parse_phandle(np, "devfreq", 0); - if (parent_np && - of_device_is_available(parent_np)) { - private->dmc_support = true; - dev_warn(dev, "defer getting devfreq\n"); - } else { - dev_info(dev, "dmc is disabled\n"); - } - } else { - dev_info(dev, "devfreq is not set\n"); - } - private->devfreq = NULL; - } else { - private->dmc_support = true; - dev_info(dev, "devfreq is ready\n"); - } - private->hdmi_pll.pll = devm_clk_get(dev, "hdmi-tmds-pll"); - if (PTR_ERR(private->hdmi_pll.pll) == -ENOENT) { - private->hdmi_pll.pll = NULL; - } else if (PTR_ERR(private->hdmi_pll.pll) == -EPROBE_DEFER) { + INIT_LIST_HEAD(&private->psr_list); + mutex_init(&private->psr_list_lock); + mutex_init(&private->commit_lock); + + private->hdmi_pll.pll = devm_clk_get_optional(dev, "hdmi-tmds-pll"); + if (PTR_ERR(private->hdmi_pll.pll) == -EPROBE_DEFER) { ret = -EPROBE_DEFER; goto err_free; } else if (IS_ERR(private->hdmi_pll.pll)) { @@ -1728,10 +1524,8 @@ ret = PTR_ERR(private->hdmi_pll.pll); goto err_free; } - private->default_pll.pll = devm_clk_get(dev, "default-vop-pll"); - if (PTR_ERR(private->default_pll.pll) == -ENOENT) { - private->default_pll.pll = NULL; - } else if (PTR_ERR(private->default_pll.pll) == -EPROBE_DEFER) { + private->default_pll.pll = devm_clk_get_optional(dev, "default-vop-pll"); + if (PTR_ERR(private->default_pll.pll) == -EPROBE_DEFER) { ret = -EPROBE_DEFER; goto err_free; } else if (IS_ERR(private->default_pll.pll)) { @@ -1740,14 +1534,9 @@ goto err_free; } - INIT_LIST_HEAD(&private->psr_list); - mutex_init(&private->psr_list_lock); - - ret = rockchip_drm_init_iommu(drm_dev); + ret = drmm_mode_config_init(drm_dev); if (ret) goto err_free; - - drm_mode_config_init(drm_dev); rockchip_drm_mode_config_init(drm_dev); rockchip_drm_create_properties(drm_dev); @@ -1773,48 +1562,38 @@ /* init kms poll for handling hpd */ drm_kms_helper_poll_init(drm_dev); - private->page_pools = dmabuf_page_pool_create(GFP_HIGHUSER | __GFP_ZERO | __GFP_COMP, 0); + ret = rockchip_drm_init_iommu(drm_dev); + if (ret) + goto err_unbind_all; rockchip_gem_pool_init(drm_dev); -#ifndef MODULE - show_loader_logo(drm_dev); -#endif ret = of_reserved_mem_device_init(drm_dev->dev); if (ret) DRM_DEBUG_KMS("No reserved memory region assign to drm\n"); + rockchip_drm_show_logo(drm_dev); + ret = rockchip_drm_fbdev_init(drm_dev); if (ret) - goto err_kms_helper_poll_fini; - - if (private->fbdev_helper && private->fbdev_helper->fb) { - drm_for_each_crtc(crtc, drm_dev) { - struct rockchip_crtc_state *s = NULL; - - s = to_rockchip_crtc_state(crtc->state); - if (is_support_hotplug(s->output_type)) - drm_framebuffer_get(private->fbdev_helper->fb); - } - } + goto err_iommu_cleanup; drm_dev->mode_config.allow_fb_modifiers = true; ret = drm_dev_register(drm_dev, 0); if (ret) - goto err_fbdev_fini; + goto err_kms_helper_poll_fini; return 0; -err_fbdev_fini: - rockchip_drm_fbdev_fini(drm_dev); err_kms_helper_poll_fini: rockchip_gem_pool_destroy(drm_dev); drm_kms_helper_poll_fini(drm_dev); + rockchip_drm_fbdev_fini(drm_dev); +err_iommu_cleanup: + rockchip_iommu_cleanup(drm_dev); err_unbind_all: - dmabuf_page_pool_destroy(private->page_pools); component_unbind_all(dev, drm_dev); err_mode_config_cleanup: drm_mode_config_cleanup(drm_dev); - rockchip_iommu_cleanup(drm_dev); err_free: drm_dev->dev_private = NULL; dev_set_drvdata(dev, NULL); @@ -1971,61 +1750,20 @@ return rockchip_gem_prime_end_cpu_access(obj, dir); } -static int rockchip_drm_gem_begin_cpu_access_partial( - struct dma_buf *dma_buf, - enum dma_data_direction dir, - unsigned int offset, unsigned int len) -{ - struct drm_gem_object *obj = dma_buf->priv; - - return rockchip_gem_prime_begin_cpu_access_partial(obj, dir, offset, len); -} - -static int rockchip_drm_gem_end_cpu_access_partial( - struct dma_buf *dma_buf, - enum dma_data_direction dir, - unsigned int offset, unsigned int len) -{ - struct drm_gem_object *obj = dma_buf->priv; - - return rockchip_gem_prime_end_cpu_access_partial(obj, dir, offset, len); -} - static const struct dma_buf_ops rockchip_drm_gem_prime_dmabuf_ops = { + .cache_sgt_mapping = true, .attach = drm_gem_map_attach, .detach = drm_gem_map_detach, .map_dma_buf = drm_gem_map_dma_buf, .unmap_dma_buf = drm_gem_unmap_dma_buf, .release = drm_gem_dmabuf_release, - .map = drm_gem_dmabuf_kmap, - .unmap = drm_gem_dmabuf_kunmap, .mmap = drm_gem_dmabuf_mmap, .vmap = drm_gem_dmabuf_vmap, .vunmap = drm_gem_dmabuf_vunmap, + .get_uuid = drm_gem_dmabuf_get_uuid, .begin_cpu_access = rockchip_drm_gem_dmabuf_begin_cpu_access, .end_cpu_access = rockchip_drm_gem_dmabuf_end_cpu_access, - .begin_cpu_access_partial = rockchip_drm_gem_begin_cpu_access_partial, - .end_cpu_access_partial = rockchip_drm_gem_end_cpu_access_partial, }; - -#ifdef CONFIG_ARCH_ROCKCHIP -static void drm_gem_prime_dmabuf_release_callback(void *data) -{ - struct drm_prime_callback_data *cb_data = data; - - if (cb_data && cb_data->obj && cb_data->obj->import_attach) { - struct dma_buf_attachment *attach = cb_data->obj->import_attach; - struct sg_table *sgt = cb_data->sgt; - - if (sgt) - dma_buf_unmap_attachment(attach, sgt, - DMA_BIDIRECTIONAL); - dma_buf_detach(attach->dmabuf, attach); - drm_gem_object_put_unlocked(cb_data->obj); - kfree(cb_data); - } -} -#endif static struct drm_gem_object *rockchip_drm_gem_prime_import_dev(struct drm_device *dev, struct dma_buf *dma_buf, @@ -2034,9 +1772,6 @@ struct dma_buf_attachment *attach; struct sg_table *sgt; struct drm_gem_object *obj; -#ifdef CONFIG_ARCH_ROCKCHIP - struct drm_prime_callback_data *cb_data = NULL; -#endif int ret; if (dma_buf->ops == &rockchip_drm_gem_prime_dmabuf_ops) { @@ -2051,15 +1786,6 @@ } } -#ifdef CONFIG_ARCH_ROCKCHIP - cb_data = dma_buf_get_release_callback_data(dma_buf, - drm_gem_prime_dmabuf_release_callback); - if (cb_data && cb_data->obj && cb_data->obj->dev == dev) { - drm_gem_object_get(cb_data->obj); - return cb_data->obj; - } -#endif - if (!dev->driver->gem_prime_import_sg_table) return ERR_PTR(-EINVAL); @@ -2068,14 +1794,6 @@ return ERR_CAST(attach); get_dma_buf(dma_buf); - -#ifdef CONFIG_ARCH_ROCKCHIP - cb_data = kmalloc(sizeof(*cb_data), GFP_KERNEL); - if (!cb_data) { - ret = -ENOMEM; - goto fail_detach; - } -#endif sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); if (IS_ERR(sgt)) { @@ -2090,24 +1808,13 @@ } obj->import_attach = attach; - -#ifdef CONFIG_ARCH_ROCKCHIP - cb_data->obj = obj; - cb_data->sgt = sgt; - dma_buf_set_release_callback(dma_buf, - drm_gem_prime_dmabuf_release_callback, cb_data); - dma_buf_put(dma_buf); - drm_gem_object_get(obj); -#endif + obj->resv = dma_buf->resv; return obj; fail_unmap: dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL); fail_detach: -#ifdef CONFIG_ARCH_ROCKCHIP - kfree(cb_data); -#endif dma_buf_detach(dma_buf, attach); dma_buf_put(dma_buf); @@ -2120,10 +1827,10 @@ return rockchip_drm_gem_prime_import_dev(dev, dma_buf, dev->dev); } -static struct dma_buf *rockchip_drm_gem_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, +static struct dma_buf *rockchip_drm_gem_prime_export(struct drm_gem_object *obj, int flags) { + struct drm_device *dev = obj->dev; struct dma_buf_export_info exp_info = { .exp_name = KBUILD_MODNAME, /* white lie for debug */ .owner = dev->driver->fops->owner, @@ -2131,26 +1838,20 @@ .size = obj->size, .flags = flags, .priv = obj, + .resv = obj->resv, }; - - if (dev->driver->gem_prime_res_obj) - exp_info.resv = dev->driver->gem_prime_res_obj(obj); return drm_gem_dmabuf_export(dev, &exp_info); } static struct drm_driver rockchip_drm_driver = { - .driver_features = DRIVER_MODESET | DRIVER_GEM | - DRIVER_PRIME | DRIVER_ATOMIC | - DRIVER_RENDER, + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC | DRIVER_RENDER, .postclose = rockchip_drm_postclose, .lastclose = rockchip_drm_lastclose, .open = rockchip_drm_open, .gem_vm_ops = &drm_gem_cma_vm_ops, .gem_free_object_unlocked = rockchip_gem_free_object, .dumb_create = rockchip_gem_dumb_create, - .dumb_map_offset = rockchip_gem_dumb_map_offset, - .dumb_destroy = drm_gem_dumb_destroy, .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import = rockchip_drm_gem_prime_import, @@ -2171,64 +1872,21 @@ .date = DRIVER_DATE, .major = DRIVER_MAJOR, .minor = DRIVER_MINOR, - .patchlevel = DRIVER_PATCH, }; #ifdef CONFIG_PM_SLEEP -static void rockchip_drm_fb_suspend(struct drm_device *drm) -{ - struct rockchip_drm_private *priv = drm->dev_private; - - console_lock(); - drm_fb_helper_set_suspend(priv->fbdev_helper, 1); - console_unlock(); -} - -static void rockchip_drm_fb_resume(struct drm_device *drm) -{ - struct rockchip_drm_private *priv = drm->dev_private; - - console_lock(); - drm_fb_helper_set_suspend(priv->fbdev_helper, 0); - console_unlock(); -} - static int rockchip_drm_sys_suspend(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); - struct rockchip_drm_private *priv; - if (!drm) - return 0; - - drm_kms_helper_poll_disable(drm); - rockchip_drm_fb_suspend(drm); - - priv = drm->dev_private; - priv->state = drm_atomic_helper_suspend(drm); - if (IS_ERR(priv->state)) { - rockchip_drm_fb_resume(drm); - drm_kms_helper_poll_enable(drm); - return PTR_ERR(priv->state); - } - - return 0; + return drm_mode_config_helper_suspend(drm); } static int rockchip_drm_sys_resume(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); - struct rockchip_drm_private *priv; - if (!drm) - return 0; - - priv = drm->dev_private; - drm_atomic_helper_resume(drm, priv->state); - rockchip_drm_fb_resume(drm); - drm_kms_helper_poll_enable(drm); - - return 0; + return drm_mode_config_helper_resume(drm); } #endif @@ -2240,6 +1898,53 @@ #define MAX_ROCKCHIP_SUB_DRIVERS 16 static struct platform_driver *rockchip_sub_drivers[MAX_ROCKCHIP_SUB_DRIVERS]; static int num_rockchip_sub_drivers; + +/* + * Check if a vop endpoint is leading to a rockchip subdriver or bridge. + * Should be called from the component bind stage of the drivers + * to ensure that all subdrivers are probed. + * + * @ep: endpoint of a rockchip vop + * + * returns true if subdriver, false if external bridge and -ENODEV + * if remote port does not contain a device. + */ +int rockchip_drm_endpoint_is_subdriver(struct device_node *ep) +{ + struct device_node *node = of_graph_get_remote_port_parent(ep); + struct platform_device *pdev; + struct device_driver *drv; + int i; + + if (!node) + return -ENODEV; + + /* status disabled will prevent creation of platform-devices */ + pdev = of_find_device_by_node(node); + of_node_put(node); + if (!pdev) + return -ENODEV; + + /* + * All rockchip subdrivers have probed at this point, so + * any device not having a driver now is an external bridge. + */ + drv = pdev->dev.driver; + if (!drv) { + platform_device_put(pdev); + return false; + } + + for (i = 0; i < num_rockchip_sub_drivers; i++) { + if (rockchip_sub_drivers[i] == to_platform_driver(drv)) { + platform_device_put(pdev); + return true; + } + } + + platform_device_put(pdev); + return false; +} static int compare_dev(struct device *dev, void *data) { @@ -2264,8 +1969,7 @@ struct device *p = NULL, *d; do { - d = bus_find_device(&platform_bus_type, p, &drv->driver, - (void *)platform_bus_type.match); + d = platform_find_device_by_driver(p, &drv->driver); put_device(p); p = d; @@ -2311,7 +2015,7 @@ } iommu = of_parse_phandle(port->parent, "iommus", 0); - if (!iommu || !of_device_is_available(iommu->parent)) { + if (!iommu || !of_device_is_available(iommu)) { DRM_DEV_DEBUG(dev, "no iommu attached for %pOF, using non-iommu buffers\n", port->parent); @@ -2323,8 +2027,8 @@ } found = true; - iommu_reserve_map |= of_property_read_bool(iommu, "rockchip,reserve-map"); + iommu_reserve_map |= of_property_read_bool(iommu, "rockchip,reserve-map"); of_node_put(iommu); of_node_put(port); } @@ -2359,14 +2063,19 @@ if (IS_ERR(match)) return PTR_ERR(match); + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (ret) + goto err; + ret = component_master_add_with_match(dev, &rockchip_drm_ops, match); - if (ret < 0) { - rockchip_drm_match_remove(dev); - return ret; - } - dev->coherent_dma_mask = DMA_BIT_MASK(64); + if (ret < 0) + goto err; return 0; +err: + rockchip_drm_match_remove(dev); + + return ret; } static int rockchip_drm_platform_remove(struct platform_device *pdev) @@ -2382,10 +2091,8 @@ { struct drm_device *drm = platform_get_drvdata(pdev); - if (drm) { - drm_kms_helper_poll_fini(drm); + if (drm) drm_atomic_helper_shutdown(drm); - } } static const struct of_device_id rockchip_drm_dt_ids[] = { @@ -2421,6 +2128,7 @@ #else ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP); ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_ROCKCHIP_VOP2); + ADD_ROCKCHIP_SUB_DRIVER(vconn_platform_driver, CONFIG_ROCKCHIP_VCONN); ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver, CONFIG_ROCKCHIP_LVDS); ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver, @@ -2428,12 +2136,17 @@ ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP); ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_rockchip_pltfm_driver, CONFIG_ROCKCHIP_DW_HDMI); - ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_driver, + ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_rockchip_driver, + CONFIG_ROCKCHIP_DW_MIPI_DSI); + ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi2_rockchip_driver, CONFIG_ROCKCHIP_DW_MIPI_DSI); ADD_ROCKCHIP_SUB_DRIVER(inno_hdmi_driver, CONFIG_ROCKCHIP_INNO_HDMI); - ADD_ROCKCHIP_SUB_DRIVER(rockchip_tve_driver, - CONFIG_ROCKCHIP_DRM_TVE); + ADD_ROCKCHIP_SUB_DRIVER(rk3066_hdmi_driver, + CONFIG_ROCKCHIP_RK3066_HDMI); ADD_ROCKCHIP_SUB_DRIVER(rockchip_rgb_driver, CONFIG_ROCKCHIP_RGB); + ADD_ROCKCHIP_SUB_DRIVER(rockchip_tve_driver, CONFIG_ROCKCHIP_DRM_TVE); + ADD_ROCKCHIP_SUB_DRIVER(dw_dp_driver, CONFIG_ROCKCHIP_DW_DP); + #endif ret = platform_register_drivers(rockchip_sub_drivers, num_rockchip_sub_drivers); @@ -2443,6 +2156,8 @@ ret = platform_driver_register(&rockchip_drm_platform_driver); if (ret) goto err_unreg_drivers; + + rockchip_gem_get_ddr_info(); return 0; @@ -2460,7 +2175,11 @@ num_rockchip_sub_drivers); } +#ifdef CONFIG_VIDEO_REVERSE_IMAGE +fs_initcall(rockchip_drm_init); +#else module_init(rockchip_drm_init); +#endif module_exit(rockchip_drm_fini); MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>"); -- Gitblit v1.6.2