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