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