malidp_mw.c 7.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// SPDX-License-Identifier: GPL-2.0
/*
 * (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
 * Author: Brian Starkey <brian.starkey@arm.com>
 *
 * ARM Mali DP Writeback connector implementation
 */
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
11
#include <drm/drm_probe_helper.h>
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drmP.h>
#include <drm/drm_writeback.h>

#include "malidp_drv.h"
#include "malidp_hw.h"
#include "malidp_mw.h"

#define to_mw_state(_state) (struct malidp_mw_connector_state *)(_state)

struct malidp_mw_connector_state {
	struct drm_connector_state base;
	dma_addr_t addrs[2];
	s32 pitches[2];
	u8 format;
	u8 n_planes;
29 30
	bool rgb2yuv_initialized;
	const s16 *rgb2yuv_coeffs;
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
};

static int malidp_mw_connector_get_modes(struct drm_connector *connector)
{
	struct drm_device *dev = connector->dev;

	return drm_add_modes_noedid(connector, dev->mode_config.max_width,
				    dev->mode_config.max_height);
}

static enum drm_mode_status
malidp_mw_connector_mode_valid(struct drm_connector *connector,
			       struct drm_display_mode *mode)
{
	struct drm_device *dev = connector->dev;
	struct drm_mode_config *mode_config = &dev->mode_config;
	int w = mode->hdisplay, h = mode->vdisplay;

	if ((w < mode_config->min_width) || (w > mode_config->max_width))
		return MODE_BAD_HVALUE;

	if ((h < mode_config->min_height) || (h > mode_config->max_height))
		return MODE_BAD_VVALUE;

	return MODE_OK;
}

const struct drm_connector_helper_funcs malidp_mw_connector_helper_funcs = {
	.get_modes = malidp_mw_connector_get_modes,
	.mode_valid = malidp_mw_connector_mode_valid,
};

static void malidp_mw_connector_reset(struct drm_connector *connector)
{
	struct malidp_mw_connector_state *mw_state =
		kzalloc(sizeof(*mw_state), GFP_KERNEL);

	if (connector->state)
		__drm_atomic_helper_connector_destroy_state(connector->state);

	kfree(connector->state);
	__drm_atomic_helper_connector_reset(connector, &mw_state->base);
}

static enum drm_connector_status
malidp_mw_connector_detect(struct drm_connector *connector, bool force)
{
78
	return connector_status_connected;
79 80 81 82 83 84 85 86 87 88
}

static void malidp_mw_connector_destroy(struct drm_connector *connector)
{
	drm_connector_cleanup(connector);
}

static struct drm_connector_state *
malidp_mw_connector_duplicate_state(struct drm_connector *connector)
{
89
	struct malidp_mw_connector_state *mw_state, *mw_current_state;
90 91 92 93 94 95 96 97

	if (WARN_ON(!connector->state))
		return NULL;

	mw_state = kzalloc(sizeof(*mw_state), GFP_KERNEL);
	if (!mw_state)
		return NULL;

98 99 100 101
	mw_current_state = to_mw_state(connector->state);
	mw_state->rgb2yuv_coeffs = mw_current_state->rgb2yuv_coeffs;
	mw_state->rgb2yuv_initialized = mw_current_state->rgb2yuv_initialized;

102 103 104 105 106 107 108 109 110 111 112 113 114 115
	__drm_atomic_helper_connector_duplicate_state(connector, &mw_state->base);

	return &mw_state->base;
}

static const struct drm_connector_funcs malidp_mw_connector_funcs = {
	.reset = malidp_mw_connector_reset,
	.detect = malidp_mw_connector_detect,
	.fill_modes = drm_helper_probe_single_connector_modes,
	.destroy = malidp_mw_connector_destroy,
	.atomic_duplicate_state = malidp_mw_connector_duplicate_state,
	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};

116 117 118 119 120 121 122
static const s16 rgb2yuv_coeffs_bt709_limited[MALIDP_COLORADJ_NUM_COEFFS] = {
	47,  157,   16,
	-26,  -87,  112,
	112, -102,  -10,
	16,  128,  128
};

123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
static int
malidp_mw_encoder_atomic_check(struct drm_encoder *encoder,
			       struct drm_crtc_state *crtc_state,
			       struct drm_connector_state *conn_state)
{
	struct malidp_mw_connector_state *mw_state = to_mw_state(conn_state);
	struct malidp_drm *malidp = encoder->dev->dev_private;
	struct drm_framebuffer *fb;
	int i, n_planes;

	if (!conn_state->writeback_job || !conn_state->writeback_job->fb)
		return 0;

	fb = conn_state->writeback_job->fb;
	if ((fb->width != crtc_state->mode.hdisplay) ||
	    (fb->height != crtc_state->mode.vdisplay)) {
		DRM_DEBUG_KMS("Invalid framebuffer size %ux%u\n",
				fb->width, fb->height);
		return -EINVAL;
	}

	mw_state->format =
		malidp_hw_get_format_id(&malidp->dev->hw->map, SE_MEMWRITE,
					fb->format->format);
	if (mw_state->format == MALIDP_INVALID_FORMAT_ID) {
		struct drm_format_name_buf format_name;

		DRM_DEBUG_KMS("Invalid pixel format %s\n",
			      drm_get_format_name(fb->format->format,
						  &format_name));
		return -EINVAL;
	}

	n_planes = drm_format_num_planes(fb->format->format);
	for (i = 0; i < n_planes; i++) {
		struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, i);
		/* memory write buffers are never rotated */
		u8 alignment = malidp_hw_get_pitch_align(malidp->dev, 0);

		if (fb->pitches[i] & (alignment - 1)) {
			DRM_DEBUG_KMS("Invalid pitch %u for plane %d\n",
				      fb->pitches[i], i);
			return -EINVAL;
		}
		mw_state->pitches[i] = fb->pitches[i];
		mw_state->addrs[i] = obj->paddr + fb->offsets[i];
	}
	mw_state->n_planes = n_planes;

172 173 174
	if (fb->format->is_yuv)
		mw_state->rgb2yuv_coeffs = rgb2yuv_coeffs_bt709_limited;

175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
	return 0;
}

static const struct drm_encoder_helper_funcs malidp_mw_encoder_helper_funcs = {
	.atomic_check = malidp_mw_encoder_atomic_check,
};

static u32 *get_writeback_formats(struct malidp_drm *malidp, int *n_formats)
{
	const struct malidp_hw_regmap *map = &malidp->dev->hw->map;
	u32 *formats;
	int n, i;

	formats = kcalloc(map->n_pixel_formats, sizeof(*formats),
			  GFP_KERNEL);
	if (!formats)
		return NULL;

	for (n = 0, i = 0;  i < map->n_pixel_formats; i++) {
		if (map->pixel_formats[i].layer & SE_MEMWRITE)
			formats[n++] = map->pixel_formats[i].format;
	}

	*n_formats = n;

	return formats;
}

int malidp_mw_connector_init(struct drm_device *drm)
{
	struct malidp_drm *malidp = drm->dev_private;
	u32 *formats;
	int ret, n_formats;

	if (!malidp->dev->hw->enable_memwrite)
		return 0;

	malidp->mw_connector.encoder.possible_crtcs = 1 << drm_crtc_index(&malidp->crtc);
	drm_connector_helper_add(&malidp->mw_connector.base,
				 &malidp_mw_connector_helper_funcs);

	formats = get_writeback_formats(malidp, &n_formats);
	if (!formats)
		return -ENOMEM;

	ret = drm_writeback_connector_init(drm, &malidp->mw_connector,
					   &malidp_mw_connector_funcs,
					   &malidp_mw_encoder_helper_funcs,
					   formats, n_formats);
	kfree(formats);
	if (ret)
		return ret;

	return 0;
}

void malidp_mw_atomic_commit(struct drm_device *drm,
			     struct drm_atomic_state *old_state)
{
	struct malidp_drm *malidp = drm->dev_private;
	struct drm_writeback_connector *mw_conn = &malidp->mw_connector;
	struct drm_connector_state *conn_state = mw_conn->base.state;
	struct malidp_hw_device *hwdev = malidp->dev;
	struct malidp_mw_connector_state *mw_state;

	if (!conn_state)
		return;

	mw_state = to_mw_state(conn_state);

	if (conn_state->writeback_job && conn_state->writeback_job->fb) {
		struct drm_framebuffer *fb = conn_state->writeback_job->fb;

		DRM_DEV_DEBUG_DRIVER(drm->dev,
				     "Enable memwrite %ux%u:%d %pad fmt: %u\n",
				     fb->width, fb->height,
				     mw_state->pitches[0],
				     &mw_state->addrs[0],
				     mw_state->format);

255
		drm_writeback_queue_job(mw_conn, conn_state);
256 257
		hwdev->hw->enable_memwrite(hwdev, mw_state->addrs,
					   mw_state->pitches, mw_state->n_planes,
258 259 260 261
					   fb->width, fb->height, mw_state->format,
					   !mw_state->rgb2yuv_initialized ?
					   mw_state->rgb2yuv_coeffs : NULL);
		mw_state->rgb2yuv_initialized = !!mw_state->rgb2yuv_coeffs;
262 263 264 265 266
	} else {
		DRM_DEV_DEBUG_DRIVER(drm->dev, "Disable memwrite\n");
		hwdev->hw->disable_memwrite(hwdev);
	}
}