]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/gpu/drm/rcar-du/rcar_du_kms.c
Merge tag 'virtio-next-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[karo-tx-linux.git] / drivers / gpu / drm / rcar-du / rcar_du_kms.c
1 /*
2  * rcar_du_kms.c  --  R-Car Display Unit Mode Setting
3  *
4  * Copyright (C) 2013 Renesas Corporation
5  *
6  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  */
13
14 #include <drm/drmP.h>
15 #include <drm/drm_crtc.h>
16 #include <drm/drm_crtc_helper.h>
17 #include <drm/drm_fb_cma_helper.h>
18 #include <drm/drm_gem_cma_helper.h>
19
20 #include "rcar_du_crtc.h"
21 #include "rcar_du_drv.h"
22 #include "rcar_du_kms.h"
23 #include "rcar_du_lvds.h"
24 #include "rcar_du_regs.h"
25 #include "rcar_du_vga.h"
26
27 /* -----------------------------------------------------------------------------
28  * Format helpers
29  */
30
31 static const struct rcar_du_format_info rcar_du_format_infos[] = {
32         {
33                 .fourcc = DRM_FORMAT_RGB565,
34                 .bpp = 16,
35                 .planes = 1,
36                 .pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP,
37                 .edf = PnDDCR4_EDF_NONE,
38         }, {
39                 .fourcc = DRM_FORMAT_ARGB1555,
40                 .bpp = 16,
41                 .planes = 1,
42                 .pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB,
43                 .edf = PnDDCR4_EDF_NONE,
44         }, {
45                 .fourcc = DRM_FORMAT_XRGB1555,
46                 .bpp = 16,
47                 .planes = 1,
48                 .pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB,
49                 .edf = PnDDCR4_EDF_NONE,
50         }, {
51                 .fourcc = DRM_FORMAT_XRGB8888,
52                 .bpp = 32,
53                 .planes = 1,
54                 .pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP,
55                 .edf = PnDDCR4_EDF_RGB888,
56         }, {
57                 .fourcc = DRM_FORMAT_ARGB8888,
58                 .bpp = 32,
59                 .planes = 1,
60                 .pnmr = PnMR_SPIM_ALP | PnMR_DDDF_16BPP,
61                 .edf = PnDDCR4_EDF_ARGB8888,
62         }, {
63                 .fourcc = DRM_FORMAT_UYVY,
64                 .bpp = 16,
65                 .planes = 1,
66                 .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
67                 .edf = PnDDCR4_EDF_NONE,
68         }, {
69                 .fourcc = DRM_FORMAT_YUYV,
70                 .bpp = 16,
71                 .planes = 1,
72                 .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
73                 .edf = PnDDCR4_EDF_NONE,
74         }, {
75                 .fourcc = DRM_FORMAT_NV12,
76                 .bpp = 12,
77                 .planes = 2,
78                 .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
79                 .edf = PnDDCR4_EDF_NONE,
80         }, {
81                 .fourcc = DRM_FORMAT_NV21,
82                 .bpp = 12,
83                 .planes = 2,
84                 .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
85                 .edf = PnDDCR4_EDF_NONE,
86         }, {
87                 /* In YUV 4:2:2, only NV16 is supported (NV61 isn't) */
88                 .fourcc = DRM_FORMAT_NV16,
89                 .bpp = 16,
90                 .planes = 2,
91                 .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
92                 .edf = PnDDCR4_EDF_NONE,
93         },
94 };
95
96 const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc)
97 {
98         unsigned int i;
99
100         for (i = 0; i < ARRAY_SIZE(rcar_du_format_infos); ++i) {
101                 if (rcar_du_format_infos[i].fourcc == fourcc)
102                         return &rcar_du_format_infos[i];
103         }
104
105         return NULL;
106 }
107
108 /* -----------------------------------------------------------------------------
109  * Common connector and encoder functions
110  */
111
112 struct drm_encoder *
113 rcar_du_connector_best_encoder(struct drm_connector *connector)
114 {
115         struct rcar_du_connector *rcon = to_rcar_connector(connector);
116
117         return &rcon->encoder->encoder;
118 }
119
120 void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder)
121 {
122 }
123
124 void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
125                               struct drm_display_mode *mode,
126                               struct drm_display_mode *adjusted_mode)
127 {
128         struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
129
130         rcar_du_crtc_route_output(encoder->crtc, renc->output);
131 }
132
133 void rcar_du_encoder_mode_commit(struct drm_encoder *encoder)
134 {
135 }
136
137 /* -----------------------------------------------------------------------------
138  * Frame buffer
139  */
140
141 int rcar_du_dumb_create(struct drm_file *file, struct drm_device *dev,
142                         struct drm_mode_create_dumb *args)
143 {
144         unsigned int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
145         unsigned int align;
146
147         /* The pitch must be aligned to a 16 pixels boundary. */
148         align = 16 * args->bpp / 8;
149         args->pitch = roundup(max(args->pitch, min_pitch), align);
150
151         return drm_gem_cma_dumb_create(file, dev, args);
152 }
153
154 static struct drm_framebuffer *
155 rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv,
156                   struct drm_mode_fb_cmd2 *mode_cmd)
157 {
158         const struct rcar_du_format_info *format;
159         unsigned int align;
160
161         format = rcar_du_format_info(mode_cmd->pixel_format);
162         if (format == NULL) {
163                 dev_dbg(dev->dev, "unsupported pixel format %08x\n",
164                         mode_cmd->pixel_format);
165                 return ERR_PTR(-EINVAL);
166         }
167
168         align = 16 * format->bpp / 8;
169
170         if (mode_cmd->pitches[0] & (align - 1) ||
171             mode_cmd->pitches[0] >= 8192) {
172                 dev_dbg(dev->dev, "invalid pitch value %u\n",
173                         mode_cmd->pitches[0]);
174                 return ERR_PTR(-EINVAL);
175         }
176
177         if (format->planes == 2) {
178                 if (mode_cmd->pitches[1] != mode_cmd->pitches[0]) {
179                         dev_dbg(dev->dev,
180                                 "luma and chroma pitches do not match\n");
181                         return ERR_PTR(-EINVAL);
182                 }
183         }
184
185         return drm_fb_cma_create(dev, file_priv, mode_cmd);
186 }
187
188 static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = {
189         .fb_create = rcar_du_fb_create,
190 };
191
192 int rcar_du_modeset_init(struct rcar_du_device *rcdu)
193 {
194         struct drm_device *dev = rcdu->ddev;
195         struct drm_encoder *encoder;
196         unsigned int i;
197         int ret;
198
199         drm_mode_config_init(rcdu->ddev);
200
201         rcdu->ddev->mode_config.min_width = 0;
202         rcdu->ddev->mode_config.min_height = 0;
203         rcdu->ddev->mode_config.max_width = 4095;
204         rcdu->ddev->mode_config.max_height = 2047;
205         rcdu->ddev->mode_config.funcs = &rcar_du_mode_config_funcs;
206
207         ret = rcar_du_plane_init(rcdu);
208         if (ret < 0)
209                 return ret;
210
211         for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i) {
212                 ret = rcar_du_crtc_create(rcdu, i);
213                 if (ret < 0)
214                         return ret;
215         }
216
217         rcdu->used_crtcs = 0;
218         rcdu->num_crtcs = i;
219
220         for (i = 0; i < rcdu->pdata->num_encoders; ++i) {
221                 const struct rcar_du_encoder_data *pdata =
222                         &rcdu->pdata->encoders[i];
223
224                 if (pdata->output >= ARRAY_SIZE(rcdu->crtcs)) {
225                         dev_warn(rcdu->dev,
226                                  "encoder %u references unexisting output %u, skipping\n",
227                                  i, pdata->output);
228                         continue;
229                 }
230
231                 switch (pdata->encoder) {
232                 case RCAR_DU_ENCODER_VGA:
233                         rcar_du_vga_init(rcdu, &pdata->u.vga, pdata->output);
234                         break;
235
236                 case RCAR_DU_ENCODER_LVDS:
237                         rcar_du_lvds_init(rcdu, &pdata->u.lvds, pdata->output);
238                         break;
239
240                 default:
241                         break;
242                 }
243         }
244
245         /* Set the possible CRTCs and possible clones. All encoders can be
246          * driven by the CRTC associated with the output they're connected to,
247          * as well as by CRTC 0.
248          */
249         list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
250                 struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
251
252                 encoder->possible_crtcs = (1 << 0) | (1 << renc->output);
253                 encoder->possible_clones = 1 << 0;
254         }
255
256         ret = rcar_du_plane_register(rcdu);
257         if (ret < 0)
258                 return ret;
259
260         drm_kms_helper_poll_init(rcdu->ddev);
261
262         drm_helper_disable_unused_functions(rcdu->ddev);
263
264         return 0;
265 }