1 // SPDX-License-Identifier: GPL-2.0
3 * rcar_du_writeback.c -- R-Car Display Unit Writeback Support
5 * Copyright (C) 2019 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
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>
14 #include "rcar_du_crtc.h"
15 #include "rcar_du_drv.h"
16 #include "rcar_du_kms.h"
17 #include "rcar_du_writeback.h"
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
24 struct rcar_du_wb_conn_state
{
25 struct drm_connector_state state
;
26 const struct rcar_du_format_info
*format
;
29 #define to_rcar_wb_conn_state(s) \
30 container_of(s, struct rcar_du_wb_conn_state, state)
33 * struct rcar_du_wb_job - Driver-private data for writeback jobs
34 * @sg_tables: scatter-gather tables for the framebuffer memory
36 struct rcar_du_wb_job
{
37 struct sg_table sg_tables
[3];
40 static int rcar_du_wb_conn_get_modes(struct drm_connector
*connector
)
42 struct drm_device
*dev
= connector
->dev
;
44 return drm_add_modes_noedid(connector
, dev
->mode_config
.max_width
,
45 dev
->mode_config
.max_height
);
48 static int rcar_du_wb_prepare_job(struct drm_writeback_connector
*connector
,
49 struct drm_writeback_job
*job
)
51 struct rcar_du_crtc
*rcrtc
= wb_to_rcar_crtc(connector
);
52 struct rcar_du_wb_job
*rjob
;
58 rjob
= kzalloc(sizeof(*rjob
), GFP_KERNEL
);
62 /* Map the framebuffer to the VSP. */
63 ret
= rcar_du_vsp_map_fb(rcrtc
->vsp
, job
->fb
, rjob
->sg_tables
);
73 static void rcar_du_wb_cleanup_job(struct drm_writeback_connector
*connector
,
74 struct drm_writeback_job
*job
)
76 struct rcar_du_crtc
*rcrtc
= wb_to_rcar_crtc(connector
);
77 struct rcar_du_wb_job
*rjob
= job
->priv
;
82 rcar_du_vsp_unmap_fb(rcrtc
->vsp
, job
->fb
, rjob
->sg_tables
);
86 static 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
,
92 static struct drm_connector_state
*
93 rcar_du_wb_conn_duplicate_state(struct drm_connector
*connector
)
95 struct rcar_du_wb_conn_state
*copy
;
97 if (WARN_ON(!connector
->state
))
100 copy
= kzalloc(sizeof(*copy
), GFP_KERNEL
);
104 __drm_atomic_helper_connector_duplicate_state(connector
, ©
->state
);
109 static void rcar_du_wb_conn_destroy_state(struct drm_connector
*connector
,
110 struct drm_connector_state
*state
)
112 __drm_atomic_helper_connector_destroy_state(state
);
113 kfree(to_rcar_wb_conn_state(state
));
116 static void rcar_du_wb_conn_reset(struct drm_connector
*connector
)
118 struct rcar_du_wb_conn_state
*state
;
120 if (connector
->state
) {
121 rcar_du_wb_conn_destroy_state(connector
, connector
->state
);
122 connector
->state
= NULL
;
125 state
= kzalloc(sizeof(*state
), GFP_KERNEL
);
129 __drm_atomic_helper_connector_reset(connector
, &state
->state
);
132 static 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
,
140 static 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
)
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
;
150 if (!conn_state
->writeback_job
)
153 fb
= conn_state
->writeback_job
->fb
;
156 * Verify that the framebuffer format is supported and that its size
157 * matches the current mode.
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
);
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__
,
175 static const struct drm_encoder_helper_funcs rcar_du_wb_enc_helper_funcs
= {
176 .atomic_check
= rcar_du_wb_enc_atomic_check
,
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.
183 static const u32 writeback_formats
[] = {
198 int rcar_du_writeback_init(struct rcar_du_device
*rcdu
,
199 struct rcar_du_crtc
*rcrtc
)
201 struct drm_writeback_connector
*wb_conn
= &rcrtc
->writeback
;
203 wb_conn
->encoder
.possible_crtcs
= 1 << drm_crtc_index(&rcrtc
->crtc
);
204 drm_connector_helper_add(&wb_conn
->base
,
205 &rcar_du_wb_conn_helper_funcs
);
207 return drm_writeback_connector_init(rcdu
->ddev
, wb_conn
,
208 &rcar_du_wb_conn_funcs
,
209 &rcar_du_wb_enc_helper_funcs
,
211 ARRAY_SIZE(writeback_formats
));
214 void rcar_du_writeback_setup(struct rcar_du_crtc
*rcrtc
,
215 struct vsp1_du_writeback_config
*cfg
)
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
;
223 state
= rcrtc
->writeback
.base
.state
;
224 if (!state
|| !state
->writeback_job
)
227 fb
= state
->writeback_job
->fb
;
228 rjob
= state
->writeback_job
->priv
;
229 wb_state
= to_rcar_wb_conn_state(state
);
231 cfg
->pixelformat
= wb_state
->format
->v4l2
;
232 cfg
->pitch
= fb
->pitches
[0];
234 for (i
= 0; i
< wb_state
->format
->planes
; ++i
)
235 cfg
->mem
[i
] = sg_dma_address(rjob
->sg_tables
[i
].sgl
)
238 drm_writeback_queue_job(&rcrtc
->writeback
, state
);
241 void rcar_du_writeback_complete(struct rcar_du_crtc
*rcrtc
)
243 drm_writeback_signal_completion(&rcrtc
->writeback
, 0);