18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * (C) COPYRIGHT 2016 ARM Limited. All rights reserved. 48c2ecf20Sopenharmony_ci * Author: Brian Starkey <brian.starkey@arm.com> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * ARM Mali DP Writeback connector implementation 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h> 108c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 118c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 128c2ecf20Sopenharmony_ci#include <drm/drm_fb_cma_helper.h> 138c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 148c2ecf20Sopenharmony_ci#include <drm/drm_gem_cma_helper.h> 158c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 168c2ecf20Sopenharmony_ci#include <drm/drm_writeback.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "malidp_drv.h" 198c2ecf20Sopenharmony_ci#include "malidp_hw.h" 208c2ecf20Sopenharmony_ci#include "malidp_mw.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define to_mw_state(_state) (struct malidp_mw_connector_state *)(_state) 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct malidp_mw_connector_state { 258c2ecf20Sopenharmony_ci struct drm_connector_state base; 268c2ecf20Sopenharmony_ci dma_addr_t addrs[2]; 278c2ecf20Sopenharmony_ci s32 pitches[2]; 288c2ecf20Sopenharmony_ci u8 format; 298c2ecf20Sopenharmony_ci u8 n_planes; 308c2ecf20Sopenharmony_ci bool rgb2yuv_initialized; 318c2ecf20Sopenharmony_ci const s16 *rgb2yuv_coeffs; 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic int malidp_mw_connector_get_modes(struct drm_connector *connector) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct drm_device *dev = connector->dev; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci return drm_add_modes_noedid(connector, dev->mode_config.max_width, 398c2ecf20Sopenharmony_ci dev->mode_config.max_height); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic enum drm_mode_status 438c2ecf20Sopenharmony_cimalidp_mw_connector_mode_valid(struct drm_connector *connector, 448c2ecf20Sopenharmony_ci struct drm_display_mode *mode) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci struct drm_device *dev = connector->dev; 478c2ecf20Sopenharmony_ci struct drm_mode_config *mode_config = &dev->mode_config; 488c2ecf20Sopenharmony_ci int w = mode->hdisplay, h = mode->vdisplay; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if ((w < mode_config->min_width) || (w > mode_config->max_width)) 518c2ecf20Sopenharmony_ci return MODE_BAD_HVALUE; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci if ((h < mode_config->min_height) || (h > mode_config->max_height)) 548c2ecf20Sopenharmony_ci return MODE_BAD_VVALUE; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci return MODE_OK; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs malidp_mw_connector_helper_funcs = { 608c2ecf20Sopenharmony_ci .get_modes = malidp_mw_connector_get_modes, 618c2ecf20Sopenharmony_ci .mode_valid = malidp_mw_connector_mode_valid, 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic void malidp_mw_connector_reset(struct drm_connector *connector) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct malidp_mw_connector_state *mw_state = 678c2ecf20Sopenharmony_ci kzalloc(sizeof(*mw_state), GFP_KERNEL); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (connector->state) 708c2ecf20Sopenharmony_ci __drm_atomic_helper_connector_destroy_state(connector->state); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci kfree(connector->state); 738c2ecf20Sopenharmony_ci connector->state = NULL; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (mw_state) 768c2ecf20Sopenharmony_ci __drm_atomic_helper_connector_reset(connector, &mw_state->base); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic enum drm_connector_status 808c2ecf20Sopenharmony_cimalidp_mw_connector_detect(struct drm_connector *connector, bool force) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci return connector_status_connected; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic void malidp_mw_connector_destroy(struct drm_connector *connector) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci drm_connector_cleanup(connector); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic struct drm_connector_state * 918c2ecf20Sopenharmony_cimalidp_mw_connector_duplicate_state(struct drm_connector *connector) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct malidp_mw_connector_state *mw_state, *mw_current_state; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (WARN_ON(!connector->state)) 968c2ecf20Sopenharmony_ci return NULL; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci mw_state = kzalloc(sizeof(*mw_state), GFP_KERNEL); 998c2ecf20Sopenharmony_ci if (!mw_state) 1008c2ecf20Sopenharmony_ci return NULL; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci mw_current_state = to_mw_state(connector->state); 1038c2ecf20Sopenharmony_ci mw_state->rgb2yuv_coeffs = mw_current_state->rgb2yuv_coeffs; 1048c2ecf20Sopenharmony_ci mw_state->rgb2yuv_initialized = mw_current_state->rgb2yuv_initialized; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci __drm_atomic_helper_connector_duplicate_state(connector, &mw_state->base); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return &mw_state->base; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs malidp_mw_connector_funcs = { 1128c2ecf20Sopenharmony_ci .reset = malidp_mw_connector_reset, 1138c2ecf20Sopenharmony_ci .detect = malidp_mw_connector_detect, 1148c2ecf20Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 1158c2ecf20Sopenharmony_ci .destroy = malidp_mw_connector_destroy, 1168c2ecf20Sopenharmony_ci .atomic_duplicate_state = malidp_mw_connector_duplicate_state, 1178c2ecf20Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic const s16 rgb2yuv_coeffs_bt709_limited[MALIDP_COLORADJ_NUM_COEFFS] = { 1218c2ecf20Sopenharmony_ci 47, 157, 16, 1228c2ecf20Sopenharmony_ci -26, -87, 112, 1238c2ecf20Sopenharmony_ci 112, -102, -10, 1248c2ecf20Sopenharmony_ci 16, 128, 128 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int 1288c2ecf20Sopenharmony_cimalidp_mw_encoder_atomic_check(struct drm_encoder *encoder, 1298c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state, 1308c2ecf20Sopenharmony_ci struct drm_connector_state *conn_state) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct malidp_mw_connector_state *mw_state = to_mw_state(conn_state); 1338c2ecf20Sopenharmony_ci struct malidp_drm *malidp = encoder->dev->dev_private; 1348c2ecf20Sopenharmony_ci struct drm_framebuffer *fb; 1358c2ecf20Sopenharmony_ci int i, n_planes; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (!conn_state->writeback_job) 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci fb = conn_state->writeback_job->fb; 1418c2ecf20Sopenharmony_ci if ((fb->width != crtc_state->mode.hdisplay) || 1428c2ecf20Sopenharmony_ci (fb->height != crtc_state->mode.vdisplay)) { 1438c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Invalid framebuffer size %ux%u\n", 1448c2ecf20Sopenharmony_ci fb->width, fb->height); 1458c2ecf20Sopenharmony_ci return -EINVAL; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (fb->modifier) { 1498c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Writeback framebuffer does not support modifiers\n"); 1508c2ecf20Sopenharmony_ci return -EINVAL; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci mw_state->format = 1548c2ecf20Sopenharmony_ci malidp_hw_get_format_id(&malidp->dev->hw->map, SE_MEMWRITE, 1558c2ecf20Sopenharmony_ci fb->format->format, !!fb->modifier); 1568c2ecf20Sopenharmony_ci if (mw_state->format == MALIDP_INVALID_FORMAT_ID) { 1578c2ecf20Sopenharmony_ci struct drm_format_name_buf format_name; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Invalid pixel format %s\n", 1608c2ecf20Sopenharmony_ci drm_get_format_name(fb->format->format, 1618c2ecf20Sopenharmony_ci &format_name)); 1628c2ecf20Sopenharmony_ci return -EINVAL; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci n_planes = fb->format->num_planes; 1668c2ecf20Sopenharmony_ci for (i = 0; i < n_planes; i++) { 1678c2ecf20Sopenharmony_ci struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, i); 1688c2ecf20Sopenharmony_ci /* memory write buffers are never rotated */ 1698c2ecf20Sopenharmony_ci u8 alignment = malidp_hw_get_pitch_align(malidp->dev, 0); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (fb->pitches[i] & (alignment - 1)) { 1728c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Invalid pitch %u for plane %d\n", 1738c2ecf20Sopenharmony_ci fb->pitches[i], i); 1748c2ecf20Sopenharmony_ci return -EINVAL; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci mw_state->pitches[i] = fb->pitches[i]; 1778c2ecf20Sopenharmony_ci mw_state->addrs[i] = obj->paddr + fb->offsets[i]; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci mw_state->n_planes = n_planes; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (fb->format->is_yuv) 1828c2ecf20Sopenharmony_ci mw_state->rgb2yuv_coeffs = rgb2yuv_coeffs_bt709_limited; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci return 0; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic const struct drm_encoder_helper_funcs malidp_mw_encoder_helper_funcs = { 1888c2ecf20Sopenharmony_ci .atomic_check = malidp_mw_encoder_atomic_check, 1898c2ecf20Sopenharmony_ci}; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic u32 *get_writeback_formats(struct malidp_drm *malidp, int *n_formats) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci const struct malidp_hw_regmap *map = &malidp->dev->hw->map; 1948c2ecf20Sopenharmony_ci u32 *formats; 1958c2ecf20Sopenharmony_ci int n, i; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci formats = kcalloc(map->n_pixel_formats, sizeof(*formats), 1988c2ecf20Sopenharmony_ci GFP_KERNEL); 1998c2ecf20Sopenharmony_ci if (!formats) 2008c2ecf20Sopenharmony_ci return NULL; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci for (n = 0, i = 0; i < map->n_pixel_formats; i++) { 2038c2ecf20Sopenharmony_ci if (map->pixel_formats[i].layer & SE_MEMWRITE) 2048c2ecf20Sopenharmony_ci formats[n++] = map->pixel_formats[i].format; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci *n_formats = n; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return formats; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ciint malidp_mw_connector_init(struct drm_device *drm) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct malidp_drm *malidp = drm->dev_private; 2158c2ecf20Sopenharmony_ci u32 *formats; 2168c2ecf20Sopenharmony_ci int ret, n_formats; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (!malidp->dev->hw->enable_memwrite) 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci malidp->mw_connector.encoder.possible_crtcs = 1 << drm_crtc_index(&malidp->crtc); 2228c2ecf20Sopenharmony_ci drm_connector_helper_add(&malidp->mw_connector.base, 2238c2ecf20Sopenharmony_ci &malidp_mw_connector_helper_funcs); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci formats = get_writeback_formats(malidp, &n_formats); 2268c2ecf20Sopenharmony_ci if (!formats) 2278c2ecf20Sopenharmony_ci return -ENOMEM; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci ret = drm_writeback_connector_init(drm, &malidp->mw_connector, 2308c2ecf20Sopenharmony_ci &malidp_mw_connector_funcs, 2318c2ecf20Sopenharmony_ci &malidp_mw_encoder_helper_funcs, 2328c2ecf20Sopenharmony_ci formats, n_formats); 2338c2ecf20Sopenharmony_ci kfree(formats); 2348c2ecf20Sopenharmony_ci if (ret) 2358c2ecf20Sopenharmony_ci return ret; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci return 0; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_civoid malidp_mw_atomic_commit(struct drm_device *drm, 2418c2ecf20Sopenharmony_ci struct drm_atomic_state *old_state) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct malidp_drm *malidp = drm->dev_private; 2448c2ecf20Sopenharmony_ci struct drm_writeback_connector *mw_conn = &malidp->mw_connector; 2458c2ecf20Sopenharmony_ci struct drm_connector_state *conn_state = mw_conn->base.state; 2468c2ecf20Sopenharmony_ci struct malidp_hw_device *hwdev = malidp->dev; 2478c2ecf20Sopenharmony_ci struct malidp_mw_connector_state *mw_state; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (!conn_state) 2508c2ecf20Sopenharmony_ci return; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci mw_state = to_mw_state(conn_state); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (conn_state->writeback_job) { 2558c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = conn_state->writeback_job->fb; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci DRM_DEV_DEBUG_DRIVER(drm->dev, 2588c2ecf20Sopenharmony_ci "Enable memwrite %ux%u:%d %pad fmt: %u\n", 2598c2ecf20Sopenharmony_ci fb->width, fb->height, 2608c2ecf20Sopenharmony_ci mw_state->pitches[0], 2618c2ecf20Sopenharmony_ci &mw_state->addrs[0], 2628c2ecf20Sopenharmony_ci mw_state->format); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci drm_writeback_queue_job(mw_conn, conn_state); 2658c2ecf20Sopenharmony_ci hwdev->hw->enable_memwrite(hwdev, mw_state->addrs, 2668c2ecf20Sopenharmony_ci mw_state->pitches, mw_state->n_planes, 2678c2ecf20Sopenharmony_ci fb->width, fb->height, mw_state->format, 2688c2ecf20Sopenharmony_ci !mw_state->rgb2yuv_initialized ? 2698c2ecf20Sopenharmony_ci mw_state->rgb2yuv_coeffs : NULL); 2708c2ecf20Sopenharmony_ci mw_state->rgb2yuv_initialized = !!mw_state->rgb2yuv_coeffs; 2718c2ecf20Sopenharmony_ci } else { 2728c2ecf20Sopenharmony_ci DRM_DEV_DEBUG_DRIVER(drm->dev, "Disable memwrite\n"); 2738c2ecf20Sopenharmony_ci hwdev->hw->disable_memwrite(hwdev); 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci} 276