Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm: rcar-du: Add writeback support for R-Car Gen3

Implement writeback support for R-Car Gen3 by exposing writeback
connectors. Behind the scene the calls are forwarded to the VSP
backend.

Using writeback connectors will allow implemented writeback support for
R-Car Gen2 with a consistent API if desired.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>

+317 -3
+4
drivers/gpu/drm/rcar-du/Kconfig
··· 36 36 depends on VIDEO_RENESAS_VSP1=y || (VIDEO_RENESAS_VSP1 && DRM_RCAR_DU=m) 37 37 help 38 38 Enable support to expose the R-Car VSP Compositor as KMS planes. 39 + 40 + config DRM_RCAR_WRITEBACK 41 + bool 42 + default y if ARM64
+2 -1
drivers/gpu/drm/rcar-du/Makefile
··· 4 4 rcar_du_encoder.o \ 5 5 rcar_du_group.o \ 6 6 rcar_du_kms.o \ 7 - rcar_du_plane.o 7 + rcar_du_plane.o \ 8 8 9 9 rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_of.o \ 10 10 rcar_du_of_lvds_r8a7790.dtb.o \ ··· 13 13 rcar_du_of_lvds_r8a7795.dtb.o \ 14 14 rcar_du_of_lvds_r8a7796.dtb.o 15 15 rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o 16 + rcar-du-drm-$(CONFIG_DRM_RCAR_WRITEBACK) += rcar_du_writeback.o 16 17 17 18 obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o 18 19 obj-$(CONFIG_DRM_RCAR_DW_HDMI) += rcar_dw_hdmi.o
+6 -1
drivers/gpu/drm/rcar-du/rcar_du_crtc.c
··· 648 648 rstate->outputs = 0; 649 649 650 650 drm_for_each_encoder_mask(encoder, crtc->dev, state->encoder_mask) { 651 - struct rcar_du_encoder *renc = to_rcar_encoder(encoder); 651 + struct rcar_du_encoder *renc; 652 652 653 + /* Skip the writeback encoder. */ 654 + if (encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL) 655 + continue; 656 + 657 + renc = to_rcar_encoder(encoder); 653 658 rstate->outputs |= BIT(renc->output); 654 659 } 655 660
+6 -1
drivers/gpu/drm/rcar-du/rcar_du_crtc.h
··· 15 15 #include <linux/wait.h> 16 16 17 17 #include <drm/drm_crtc.h> 18 + #include <drm/drm_writeback.h> 18 19 19 20 #include <media/vsp1.h> 20 21 ··· 40 39 * @group: CRTC group this CRTC belongs to 41 40 * @vsp: VSP feeding video to this CRTC 42 41 * @vsp_pipe: index of the VSP pipeline feeding video to this CRTC 42 + * @writeback: the writeback connector 43 43 */ 44 44 struct rcar_du_crtc { 45 45 struct drm_crtc crtc; ··· 67 65 68 66 const char *const *sources; 69 67 unsigned int sources_count; 68 + 69 + struct drm_writeback_connector writeback; 70 70 }; 71 71 72 - #define to_rcar_crtc(c) container_of(c, struct rcar_du_crtc, crtc) 72 + #define to_rcar_crtc(c) container_of(c, struct rcar_du_crtc, crtc) 73 + #define wb_to_rcar_crtc(c) container_of(c, struct rcar_du_crtc, writeback) 73 74 74 75 /** 75 76 * struct rcar_du_crtc_state - Driver-specific CRTC state
+12
drivers/gpu/drm/rcar-du/rcar_du_kms.c
··· 26 26 #include "rcar_du_kms.h" 27 27 #include "rcar_du_regs.h" 28 28 #include "rcar_du_vsp.h" 29 + #include "rcar_du_writeback.h" 29 30 30 31 /* ----------------------------------------------------------------------------- 31 32 * Format helpers ··· 663 662 664 663 encoder->possible_crtcs = route->possible_crtcs; 665 664 encoder->possible_clones = (1 << num_encoders) - 1; 665 + } 666 + 667 + /* Create the writeback connectors. */ 668 + if (rcdu->info->gen >= 3) { 669 + for (i = 0; i < rcdu->num_crtcs; ++i) { 670 + struct rcar_du_crtc *rcrtc = &rcdu->crtcs[i]; 671 + 672 + ret = rcar_du_writeback_init(rcdu, rcrtc); 673 + if (ret < 0) 674 + return ret; 675 + } 666 676 } 667 677 668 678 /*
+5
drivers/gpu/drm/rcar-du/rcar_du_vsp.c
··· 27 27 #include "rcar_du_drv.h" 28 28 #include "rcar_du_kms.h" 29 29 #include "rcar_du_vsp.h" 30 + #include "rcar_du_writeback.h" 30 31 31 32 static void rcar_du_vsp_complete(void *private, unsigned int status, u32 crc) 32 33 { ··· 38 37 39 38 if (status & VSP1_DU_STATUS_COMPLETE) 40 39 rcar_du_crtc_finish_page_flip(crtc); 40 + if (status & VSP1_DU_STATUS_WRITEBACK) 41 + rcar_du_writeback_complete(crtc); 41 42 42 43 drm_crtc_add_crc_entry(&crtc->crtc, false, 0, &crc); 43 44 } ··· 110 107 111 108 state = to_rcar_crtc_state(crtc->crtc.state); 112 109 cfg.crc = state->crc; 110 + 111 + rcar_du_writeback_setup(crtc, &cfg.writeback); 113 112 114 113 vsp1_du_atomic_flush(crtc->vsp->vsp, crtc->vsp_pipe, &cfg); 115 114 }
+243
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 + }
+39
drivers/gpu/drm/rcar-du/rcar_du_writeback.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0+ */ 2 + /* 3 + * rcar_du_writeback.h -- R-Car Display Unit Writeback Support 4 + * 5 + * Copyright (C) 2019 Laurent Pinchart <laurent.pinchart@ideasonboard.com> 6 + */ 7 + 8 + #ifndef __RCAR_DU_WRITEBACK_H__ 9 + #define __RCAR_DU_WRITEBACK_H__ 10 + 11 + #include <drm/drm_plane.h> 12 + 13 + struct rcar_du_crtc; 14 + struct rcar_du_device; 15 + struct vsp1_du_atomic_pipe_config; 16 + 17 + #ifdef CONFIG_DRM_RCAR_WRITEBACK 18 + int rcar_du_writeback_init(struct rcar_du_device *rcdu, 19 + struct rcar_du_crtc *rcrtc); 20 + void rcar_du_writeback_setup(struct rcar_du_crtc *rcrtc, 21 + struct vsp1_du_writeback_config *cfg); 22 + void rcar_du_writeback_complete(struct rcar_du_crtc *rcrtc); 23 + #else 24 + static inline int rcar_du_writeback_init(struct rcar_du_device *rcdu, 25 + struct rcar_du_crtc *rcrtc) 26 + { 27 + return -ENXIO; 28 + } 29 + static inline void 30 + rcar_du_writeback_setup(struct rcar_du_crtc *rcrtc, 31 + struct vsp1_du_writeback_config *cfg) 32 + { 33 + } 34 + static inline void rcar_du_writeback_complete(struct rcar_du_crtc *rcrtc) 35 + { 36 + } 37 + #endif 38 + 39 + #endif /* __RCAR_DU_WRITEBACK_H__ */