18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * rcar_du_writeback.c -- R-Car Display Unit Writeback Support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2019 Laurent Pinchart <laurent.pinchart@ideasonboard.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 98c2ecf20Sopenharmony_ci#include <drm/drm_device.h> 108c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 118c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 128c2ecf20Sopenharmony_ci#include <drm/drm_writeback.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "rcar_du_crtc.h" 158c2ecf20Sopenharmony_ci#include "rcar_du_drv.h" 168c2ecf20Sopenharmony_ci#include "rcar_du_kms.h" 178c2ecf20Sopenharmony_ci#include "rcar_du_writeback.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/** 208c2ecf20Sopenharmony_ci * struct rcar_du_wb_conn_state - Driver-specific writeback connector state 218c2ecf20Sopenharmony_ci * @state: base DRM connector state 228c2ecf20Sopenharmony_ci * @format: format of the writeback framebuffer 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_cistruct rcar_du_wb_conn_state { 258c2ecf20Sopenharmony_ci struct drm_connector_state state; 268c2ecf20Sopenharmony_ci const struct rcar_du_format_info *format; 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define to_rcar_wb_conn_state(s) \ 308c2ecf20Sopenharmony_ci container_of(s, struct rcar_du_wb_conn_state, state) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/** 338c2ecf20Sopenharmony_ci * struct rcar_du_wb_job - Driver-private data for writeback jobs 348c2ecf20Sopenharmony_ci * @sg_tables: scatter-gather tables for the framebuffer memory 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_cistruct rcar_du_wb_job { 378c2ecf20Sopenharmony_ci struct sg_table sg_tables[3]; 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int rcar_du_wb_conn_get_modes(struct drm_connector *connector) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct drm_device *dev = connector->dev; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci return drm_add_modes_noedid(connector, dev->mode_config.max_width, 458c2ecf20Sopenharmony_ci dev->mode_config.max_height); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int rcar_du_wb_prepare_job(struct drm_writeback_connector *connector, 498c2ecf20Sopenharmony_ci struct drm_writeback_job *job) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct rcar_du_crtc *rcrtc = wb_to_rcar_crtc(connector); 528c2ecf20Sopenharmony_ci struct rcar_du_wb_job *rjob; 538c2ecf20Sopenharmony_ci int ret; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (!job->fb) 568c2ecf20Sopenharmony_ci return 0; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci rjob = kzalloc(sizeof(*rjob), GFP_KERNEL); 598c2ecf20Sopenharmony_ci if (!rjob) 608c2ecf20Sopenharmony_ci return -ENOMEM; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci /* Map the framebuffer to the VSP. */ 638c2ecf20Sopenharmony_ci ret = rcar_du_vsp_map_fb(rcrtc->vsp, job->fb, rjob->sg_tables); 648c2ecf20Sopenharmony_ci if (ret < 0) { 658c2ecf20Sopenharmony_ci kfree(rjob); 668c2ecf20Sopenharmony_ci return ret; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci job->priv = rjob; 708c2ecf20Sopenharmony_ci return 0; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void rcar_du_wb_cleanup_job(struct drm_writeback_connector *connector, 748c2ecf20Sopenharmony_ci struct drm_writeback_job *job) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct rcar_du_crtc *rcrtc = wb_to_rcar_crtc(connector); 778c2ecf20Sopenharmony_ci struct rcar_du_wb_job *rjob = job->priv; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (!job->fb) 808c2ecf20Sopenharmony_ci return; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci rcar_du_vsp_unmap_fb(rcrtc->vsp, job->fb, rjob->sg_tables); 838c2ecf20Sopenharmony_ci kfree(rjob); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs rcar_du_wb_conn_helper_funcs = { 878c2ecf20Sopenharmony_ci .get_modes = rcar_du_wb_conn_get_modes, 888c2ecf20Sopenharmony_ci .prepare_writeback_job = rcar_du_wb_prepare_job, 898c2ecf20Sopenharmony_ci .cleanup_writeback_job = rcar_du_wb_cleanup_job, 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic struct drm_connector_state * 938c2ecf20Sopenharmony_circar_du_wb_conn_duplicate_state(struct drm_connector *connector) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct rcar_du_wb_conn_state *copy; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (WARN_ON(!connector->state)) 988c2ecf20Sopenharmony_ci return NULL; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci copy = kzalloc(sizeof(*copy), GFP_KERNEL); 1018c2ecf20Sopenharmony_ci if (!copy) 1028c2ecf20Sopenharmony_ci return NULL; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci __drm_atomic_helper_connector_duplicate_state(connector, ©->state); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return ©->state; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void rcar_du_wb_conn_destroy_state(struct drm_connector *connector, 1108c2ecf20Sopenharmony_ci struct drm_connector_state *state) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci __drm_atomic_helper_connector_destroy_state(state); 1138c2ecf20Sopenharmony_ci kfree(to_rcar_wb_conn_state(state)); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic void rcar_du_wb_conn_reset(struct drm_connector *connector) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct rcar_du_wb_conn_state *state; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (connector->state) { 1218c2ecf20Sopenharmony_ci rcar_du_wb_conn_destroy_state(connector, connector->state); 1228c2ecf20Sopenharmony_ci connector->state = NULL; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci state = kzalloc(sizeof(*state), GFP_KERNEL); 1268c2ecf20Sopenharmony_ci if (state == NULL) 1278c2ecf20Sopenharmony_ci return; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci __drm_atomic_helper_connector_reset(connector, &state->state); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs rcar_du_wb_conn_funcs = { 1338c2ecf20Sopenharmony_ci .reset = rcar_du_wb_conn_reset, 1348c2ecf20Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 1358c2ecf20Sopenharmony_ci .destroy = drm_connector_cleanup, 1368c2ecf20Sopenharmony_ci .atomic_duplicate_state = rcar_du_wb_conn_duplicate_state, 1378c2ecf20Sopenharmony_ci .atomic_destroy_state = rcar_du_wb_conn_destroy_state, 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int rcar_du_wb_enc_atomic_check(struct drm_encoder *encoder, 1418c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state, 1428c2ecf20Sopenharmony_ci struct drm_connector_state *conn_state) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct rcar_du_wb_conn_state *wb_state = 1458c2ecf20Sopenharmony_ci to_rcar_wb_conn_state(conn_state); 1468c2ecf20Sopenharmony_ci const struct drm_display_mode *mode = &crtc_state->mode; 1478c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 1488c2ecf20Sopenharmony_ci struct drm_framebuffer *fb; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (!conn_state->writeback_job) 1518c2ecf20Sopenharmony_ci return 0; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci fb = conn_state->writeback_job->fb; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* 1568c2ecf20Sopenharmony_ci * Verify that the framebuffer format is supported and that its size 1578c2ecf20Sopenharmony_ci * matches the current mode. 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_ci if (fb->width != mode->hdisplay || fb->height != mode->vdisplay) { 1608c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "%s: invalid framebuffer size %ux%u\n", 1618c2ecf20Sopenharmony_ci __func__, fb->width, fb->height); 1628c2ecf20Sopenharmony_ci return -EINVAL; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci wb_state->format = rcar_du_format_info(fb->format->format); 1668c2ecf20Sopenharmony_ci if (wb_state->format == NULL) { 1678c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "%s: unsupported format %08x\n", __func__, 1688c2ecf20Sopenharmony_ci fb->format->format); 1698c2ecf20Sopenharmony_ci return -EINVAL; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return 0; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic const struct drm_encoder_helper_funcs rcar_du_wb_enc_helper_funcs = { 1768c2ecf20Sopenharmony_ci .atomic_check = rcar_du_wb_enc_atomic_check, 1778c2ecf20Sopenharmony_ci}; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* 1808c2ecf20Sopenharmony_ci * Only RGB formats are currently supported as the VSP outputs RGB to the DU 1818c2ecf20Sopenharmony_ci * and can't convert to YUV separately for writeback. 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_cistatic const u32 writeback_formats[] = { 1848c2ecf20Sopenharmony_ci DRM_FORMAT_RGB332, 1858c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB4444, 1868c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB4444, 1878c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB1555, 1888c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB1555, 1898c2ecf20Sopenharmony_ci DRM_FORMAT_RGB565, 1908c2ecf20Sopenharmony_ci DRM_FORMAT_BGR888, 1918c2ecf20Sopenharmony_ci DRM_FORMAT_RGB888, 1928c2ecf20Sopenharmony_ci DRM_FORMAT_BGRA8888, 1938c2ecf20Sopenharmony_ci DRM_FORMAT_BGRX8888, 1948c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB8888, 1958c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB8888, 1968c2ecf20Sopenharmony_ci}; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ciint rcar_du_writeback_init(struct rcar_du_device *rcdu, 1998c2ecf20Sopenharmony_ci struct rcar_du_crtc *rcrtc) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct drm_writeback_connector *wb_conn = &rcrtc->writeback; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci wb_conn->encoder.possible_crtcs = 1 << drm_crtc_index(&rcrtc->crtc); 2048c2ecf20Sopenharmony_ci drm_connector_helper_add(&wb_conn->base, 2058c2ecf20Sopenharmony_ci &rcar_du_wb_conn_helper_funcs); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci return drm_writeback_connector_init(rcdu->ddev, wb_conn, 2088c2ecf20Sopenharmony_ci &rcar_du_wb_conn_funcs, 2098c2ecf20Sopenharmony_ci &rcar_du_wb_enc_helper_funcs, 2108c2ecf20Sopenharmony_ci writeback_formats, 2118c2ecf20Sopenharmony_ci ARRAY_SIZE(writeback_formats)); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_civoid rcar_du_writeback_setup(struct rcar_du_crtc *rcrtc, 2158c2ecf20Sopenharmony_ci struct vsp1_du_writeback_config *cfg) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct rcar_du_wb_conn_state *wb_state; 2188c2ecf20Sopenharmony_ci struct drm_connector_state *state; 2198c2ecf20Sopenharmony_ci struct rcar_du_wb_job *rjob; 2208c2ecf20Sopenharmony_ci struct drm_framebuffer *fb; 2218c2ecf20Sopenharmony_ci unsigned int i; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci state = rcrtc->writeback.base.state; 2248c2ecf20Sopenharmony_ci if (!state || !state->writeback_job) 2258c2ecf20Sopenharmony_ci return; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci fb = state->writeback_job->fb; 2288c2ecf20Sopenharmony_ci rjob = state->writeback_job->priv; 2298c2ecf20Sopenharmony_ci wb_state = to_rcar_wb_conn_state(state); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci cfg->pixelformat = wb_state->format->v4l2; 2328c2ecf20Sopenharmony_ci cfg->pitch = fb->pitches[0]; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci for (i = 0; i < wb_state->format->planes; ++i) 2358c2ecf20Sopenharmony_ci cfg->mem[i] = sg_dma_address(rjob->sg_tables[i].sgl) 2368c2ecf20Sopenharmony_ci + fb->offsets[i]; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci drm_writeback_queue_job(&rcrtc->writeback, state); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_civoid rcar_du_writeback_complete(struct rcar_du_crtc *rcrtc) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci drm_writeback_signal_completion(&rcrtc->writeback, 0); 2448c2ecf20Sopenharmony_ci} 245