cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

rcar_du_writeback.c (6405B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * rcar_du_writeback.c  --  R-Car Display Unit Writeback Support
      4 *
      5 * Copyright (C) 2019 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
      6 */
      7
      8#include <drm/drm_atomic_helper.h>
      9#include <drm/drm_device.h>
     10#include <drm/drm_fourcc.h>
     11#include <drm/drm_probe_helper.h>
     12#include <drm/drm_writeback.h>
     13
     14#include "rcar_du_crtc.h"
     15#include "rcar_du_drv.h"
     16#include "rcar_du_kms.h"
     17#include "rcar_du_writeback.h"
     18
     19/**
     20 * struct rcar_du_wb_conn_state - Driver-specific writeback connector state
     21 * @state: base DRM connector state
     22 * @format: format of the writeback framebuffer
     23 */
     24struct rcar_du_wb_conn_state {
     25	struct drm_connector_state state;
     26	const struct rcar_du_format_info *format;
     27};
     28
     29#define to_rcar_wb_conn_state(s) \
     30	container_of(s, struct rcar_du_wb_conn_state, state)
     31
     32/**
     33 * struct rcar_du_wb_job - Driver-private data for writeback jobs
     34 * @sg_tables: scatter-gather tables for the framebuffer memory
     35 */
     36struct rcar_du_wb_job {
     37	struct sg_table sg_tables[3];
     38};
     39
     40static int rcar_du_wb_conn_get_modes(struct drm_connector *connector)
     41{
     42	struct drm_device *dev = connector->dev;
     43
     44	return drm_add_modes_noedid(connector, dev->mode_config.max_width,
     45				    dev->mode_config.max_height);
     46}
     47
     48static int rcar_du_wb_prepare_job(struct drm_writeback_connector *connector,
     49				  struct drm_writeback_job *job)
     50{
     51	struct rcar_du_crtc *rcrtc = wb_to_rcar_crtc(connector);
     52	struct rcar_du_wb_job *rjob;
     53	int ret;
     54
     55	if (!job->fb)
     56		return 0;
     57
     58	rjob = kzalloc(sizeof(*rjob), GFP_KERNEL);
     59	if (!rjob)
     60		return -ENOMEM;
     61
     62	/* Map the framebuffer to the VSP. */
     63	ret = rcar_du_vsp_map_fb(rcrtc->vsp, job->fb, rjob->sg_tables);
     64	if (ret < 0) {
     65		kfree(rjob);
     66		return ret;
     67	}
     68
     69	job->priv = rjob;
     70	return 0;
     71}
     72
     73static void rcar_du_wb_cleanup_job(struct drm_writeback_connector *connector,
     74				   struct drm_writeback_job *job)
     75{
     76	struct rcar_du_crtc *rcrtc = wb_to_rcar_crtc(connector);
     77	struct rcar_du_wb_job *rjob = job->priv;
     78
     79	if (!job->fb)
     80		return;
     81
     82	rcar_du_vsp_unmap_fb(rcrtc->vsp, job->fb, rjob->sg_tables);
     83	kfree(rjob);
     84}
     85
     86static const struct drm_connector_helper_funcs rcar_du_wb_conn_helper_funcs = {
     87	.get_modes = rcar_du_wb_conn_get_modes,
     88	.prepare_writeback_job = rcar_du_wb_prepare_job,
     89	.cleanup_writeback_job = rcar_du_wb_cleanup_job,
     90};
     91
     92static struct drm_connector_state *
     93rcar_du_wb_conn_duplicate_state(struct drm_connector *connector)
     94{
     95	struct rcar_du_wb_conn_state *copy;
     96
     97	if (WARN_ON(!connector->state))
     98		return NULL;
     99
    100	copy = kzalloc(sizeof(*copy), GFP_KERNEL);
    101	if (!copy)
    102		return NULL;
    103
    104	__drm_atomic_helper_connector_duplicate_state(connector, &copy->state);
    105
    106	return &copy->state;
    107}
    108
    109static void rcar_du_wb_conn_destroy_state(struct drm_connector *connector,
    110					  struct drm_connector_state *state)
    111{
    112	__drm_atomic_helper_connector_destroy_state(state);
    113	kfree(to_rcar_wb_conn_state(state));
    114}
    115
    116static void rcar_du_wb_conn_reset(struct drm_connector *connector)
    117{
    118	struct rcar_du_wb_conn_state *state;
    119
    120	if (connector->state) {
    121		rcar_du_wb_conn_destroy_state(connector, connector->state);
    122		connector->state = NULL;
    123	}
    124
    125	state = kzalloc(sizeof(*state), GFP_KERNEL);
    126	if (state == NULL)
    127		return;
    128
    129	__drm_atomic_helper_connector_reset(connector, &state->state);
    130}
    131
    132static const struct drm_connector_funcs rcar_du_wb_conn_funcs = {
    133	.reset = rcar_du_wb_conn_reset,
    134	.fill_modes = drm_helper_probe_single_connector_modes,
    135	.destroy = drm_connector_cleanup,
    136	.atomic_duplicate_state = rcar_du_wb_conn_duplicate_state,
    137	.atomic_destroy_state = rcar_du_wb_conn_destroy_state,
    138};
    139
    140static int rcar_du_wb_enc_atomic_check(struct drm_encoder *encoder,
    141				       struct drm_crtc_state *crtc_state,
    142				       struct drm_connector_state *conn_state)
    143{
    144	struct rcar_du_wb_conn_state *wb_state =
    145		to_rcar_wb_conn_state(conn_state);
    146	const struct drm_display_mode *mode = &crtc_state->mode;
    147	struct drm_device *dev = encoder->dev;
    148	struct drm_framebuffer *fb;
    149
    150	if (!conn_state->writeback_job)
    151		return 0;
    152
    153	fb = conn_state->writeback_job->fb;
    154
    155	/*
    156	 * Verify that the framebuffer format is supported and that its size
    157	 * matches the current mode.
    158	 */
    159	if (fb->width != mode->hdisplay || fb->height != mode->vdisplay) {
    160		dev_dbg(dev->dev, "%s: invalid framebuffer size %ux%u\n",
    161			__func__, fb->width, fb->height);
    162		return -EINVAL;
    163	}
    164
    165	wb_state->format = rcar_du_format_info(fb->format->format);
    166	if (wb_state->format == NULL) {
    167		dev_dbg(dev->dev, "%s: unsupported format %08x\n", __func__,
    168			fb->format->format);
    169		return -EINVAL;
    170	}
    171
    172	return 0;
    173}
    174
    175static const struct drm_encoder_helper_funcs rcar_du_wb_enc_helper_funcs = {
    176	.atomic_check = rcar_du_wb_enc_atomic_check,
    177};
    178
    179/*
    180 * Only RGB formats are currently supported as the VSP outputs RGB to the DU
    181 * and can't convert to YUV separately for writeback.
    182 */
    183static const u32 writeback_formats[] = {
    184	DRM_FORMAT_RGB332,
    185	DRM_FORMAT_ARGB4444,
    186	DRM_FORMAT_XRGB4444,
    187	DRM_FORMAT_ARGB1555,
    188	DRM_FORMAT_XRGB1555,
    189	DRM_FORMAT_RGB565,
    190	DRM_FORMAT_BGR888,
    191	DRM_FORMAT_RGB888,
    192	DRM_FORMAT_BGRA8888,
    193	DRM_FORMAT_BGRX8888,
    194	DRM_FORMAT_ARGB8888,
    195	DRM_FORMAT_XRGB8888,
    196};
    197
    198int rcar_du_writeback_init(struct rcar_du_device *rcdu,
    199			   struct rcar_du_crtc *rcrtc)
    200{
    201	struct drm_writeback_connector *wb_conn = &rcrtc->writeback;
    202
    203	drm_connector_helper_add(&wb_conn->base,
    204				 &rcar_du_wb_conn_helper_funcs);
    205
    206	return drm_writeback_connector_init(&rcdu->ddev, wb_conn,
    207					    &rcar_du_wb_conn_funcs,
    208					    &rcar_du_wb_enc_helper_funcs,
    209					    writeback_formats,
    210					    ARRAY_SIZE(writeback_formats),
    211					    1 << drm_crtc_index(&rcrtc->crtc));
    212}
    213
    214void rcar_du_writeback_setup(struct rcar_du_crtc *rcrtc,
    215			     struct vsp1_du_writeback_config *cfg)
    216{
    217	struct rcar_du_wb_conn_state *wb_state;
    218	struct drm_connector_state *state;
    219	struct rcar_du_wb_job *rjob;
    220	struct drm_framebuffer *fb;
    221	unsigned int i;
    222
    223	state = rcrtc->writeback.base.state;
    224	if (!state || !state->writeback_job)
    225		return;
    226
    227	fb = state->writeback_job->fb;
    228	rjob = state->writeback_job->priv;
    229	wb_state = to_rcar_wb_conn_state(state);
    230
    231	cfg->pixelformat = wb_state->format->v4l2;
    232	cfg->pitch = fb->pitches[0];
    233
    234	for (i = 0; i < wb_state->format->planes; ++i)
    235		cfg->mem[i] = sg_dma_address(rjob->sg_tables[i].sgl)
    236			    + fb->offsets[i];
    237
    238	drm_writeback_queue_job(&rcrtc->writeback, state);
    239}
    240
    241void rcar_du_writeback_complete(struct rcar_du_crtc *rcrtc)
    242{
    243	drm_writeback_signal_completion(&rcrtc->writeback, 0);
    244}