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