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, &copy->state);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return &copy->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