Commit | Line | Data |
---|---|---|
51c13278 LP |
1 | /* |
2 | * shmob_drm_plane.c -- SH Mobile DRM Planes | |
3 | * | |
4 | * Copyright (C) 2012 Renesas Corporation | |
5 | * | |
6 | * 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 <video/sh_mobile_meram.h> | |
21 | ||
22 | #include "shmob_drm_drv.h" | |
23 | #include "shmob_drm_kms.h" | |
24 | #include "shmob_drm_plane.h" | |
25 | #include "shmob_drm_regs.h" | |
26 | ||
27 | struct shmob_drm_plane { | |
28 | struct drm_plane plane; | |
29 | unsigned int index; | |
30 | unsigned int alpha; | |
31 | ||
32 | const struct shmob_drm_format_info *format; | |
33 | unsigned long dma[2]; | |
34 | ||
35 | unsigned int src_x; | |
36 | unsigned int src_y; | |
37 | unsigned int crtc_x; | |
38 | unsigned int crtc_y; | |
39 | unsigned int crtc_w; | |
40 | unsigned int crtc_h; | |
41 | }; | |
42 | ||
43 | #define to_shmob_plane(p) container_of(p, struct shmob_drm_plane, plane) | |
44 | ||
45 | static void shmob_drm_plane_compute_base(struct shmob_drm_plane *splane, | |
46 | struct drm_framebuffer *fb, | |
47 | int x, int y) | |
48 | { | |
49 | struct drm_gem_cma_object *gem; | |
50 | unsigned int bpp; | |
51 | ||
52 | bpp = splane->format->yuv ? 8 : splane->format->bpp; | |
53 | gem = drm_fb_cma_get_gem_obj(fb, 0); | |
54 | splane->dma[0] = gem->paddr + fb->offsets[0] | |
55 | + y * fb->pitches[0] + x * bpp / 8; | |
56 | ||
57 | if (splane->format->yuv) { | |
58 | bpp = splane->format->bpp - 8; | |
59 | gem = drm_fb_cma_get_gem_obj(fb, 1); | |
60 | splane->dma[1] = gem->paddr + fb->offsets[1] | |
61 | + y / (bpp == 4 ? 2 : 1) * fb->pitches[1] | |
62 | + x * (bpp == 16 ? 2 : 1); | |
63 | } | |
64 | } | |
65 | ||
66 | static void __shmob_drm_plane_setup(struct shmob_drm_plane *splane, | |
67 | struct drm_framebuffer *fb) | |
68 | { | |
69 | struct shmob_drm_device *sdev = splane->plane.dev->dev_private; | |
70 | u32 format; | |
71 | ||
72 | /* TODO: Support ROP3 mode */ | |
73 | format = LDBBSIFR_EN | (splane->alpha << LDBBSIFR_LAY_SHIFT); | |
74 | ||
75 | switch (splane->format->fourcc) { | |
76 | case DRM_FORMAT_RGB565: | |
77 | case DRM_FORMAT_NV21: | |
78 | case DRM_FORMAT_NV61: | |
79 | case DRM_FORMAT_NV42: | |
80 | format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW; | |
81 | break; | |
82 | case DRM_FORMAT_RGB888: | |
83 | case DRM_FORMAT_NV12: | |
84 | case DRM_FORMAT_NV16: | |
85 | case DRM_FORMAT_NV24: | |
86 | format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW | LDBBSIFR_SWPB; | |
87 | break; | |
88 | case DRM_FORMAT_ARGB8888: | |
89 | default: | |
90 | format |= LDBBSIFR_SWPL; | |
91 | break; | |
92 | } | |
93 | ||
94 | switch (splane->format->fourcc) { | |
95 | case DRM_FORMAT_RGB565: | |
96 | format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB16; | |
97 | break; | |
98 | case DRM_FORMAT_RGB888: | |
99 | format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB24; | |
100 | break; | |
101 | case DRM_FORMAT_ARGB8888: | |
102 | format |= LDBBSIFR_AL_PK | LDBBSIFR_RY | LDDFR_PKF_ARGB32; | |
103 | break; | |
104 | case DRM_FORMAT_NV12: | |
105 | case DRM_FORMAT_NV21: | |
106 | format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_420; | |
107 | break; | |
108 | case DRM_FORMAT_NV16: | |
109 | case DRM_FORMAT_NV61: | |
110 | format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_422; | |
111 | break; | |
112 | case DRM_FORMAT_NV24: | |
113 | case DRM_FORMAT_NV42: | |
114 | format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_444; | |
115 | break; | |
116 | } | |
117 | ||
118 | #define plane_reg_dump(sdev, splane, reg) \ | |
119 | dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x 0x%08x\n", __func__, \ | |
120 | splane->index, #reg, \ | |
121 | lcdc_read(sdev, reg(splane->index)), \ | |
122 | lcdc_read(sdev, reg(splane->index) + LCDC_SIDE_B_OFFSET)) | |
123 | ||
124 | plane_reg_dump(sdev, splane, LDBnBSIFR); | |
125 | plane_reg_dump(sdev, splane, LDBnBSSZR); | |
126 | plane_reg_dump(sdev, splane, LDBnBLOCR); | |
127 | plane_reg_dump(sdev, splane, LDBnBSMWR); | |
128 | plane_reg_dump(sdev, splane, LDBnBSAYR); | |
129 | plane_reg_dump(sdev, splane, LDBnBSACR); | |
130 | ||
131 | lcdc_write(sdev, LDBCR, LDBCR_UPC(splane->index)); | |
132 | dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x\n", __func__, splane->index, | |
133 | "LDBCR", lcdc_read(sdev, LDBCR)); | |
134 | ||
135 | lcdc_write(sdev, LDBnBSIFR(splane->index), format); | |
136 | ||
137 | lcdc_write(sdev, LDBnBSSZR(splane->index), | |
138 | (splane->crtc_h << LDBBSSZR_BVSS_SHIFT) | | |
139 | (splane->crtc_w << LDBBSSZR_BHSS_SHIFT)); | |
140 | lcdc_write(sdev, LDBnBLOCR(splane->index), | |
141 | (splane->crtc_y << LDBBLOCR_CVLC_SHIFT) | | |
142 | (splane->crtc_x << LDBBLOCR_CHLC_SHIFT)); | |
143 | lcdc_write(sdev, LDBnBSMWR(splane->index), | |
144 | fb->pitches[0] << LDBBSMWR_BSMW_SHIFT); | |
145 | ||
146 | shmob_drm_plane_compute_base(splane, fb, splane->src_x, splane->src_y); | |
147 | ||
148 | lcdc_write(sdev, LDBnBSAYR(splane->index), splane->dma[0]); | |
149 | if (splane->format->yuv) | |
150 | lcdc_write(sdev, LDBnBSACR(splane->index), splane->dma[1]); | |
151 | ||
152 | lcdc_write(sdev, LDBCR, | |
153 | LDBCR_UPF(splane->index) | LDBCR_UPD(splane->index)); | |
154 | dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x\n", __func__, splane->index, | |
155 | "LDBCR", lcdc_read(sdev, LDBCR)); | |
156 | ||
157 | plane_reg_dump(sdev, splane, LDBnBSIFR); | |
158 | plane_reg_dump(sdev, splane, LDBnBSSZR); | |
159 | plane_reg_dump(sdev, splane, LDBnBLOCR); | |
160 | plane_reg_dump(sdev, splane, LDBnBSMWR); | |
161 | plane_reg_dump(sdev, splane, LDBnBSAYR); | |
162 | plane_reg_dump(sdev, splane, LDBnBSACR); | |
163 | } | |
164 | ||
165 | void shmob_drm_plane_setup(struct drm_plane *plane) | |
166 | { | |
167 | struct shmob_drm_plane *splane = to_shmob_plane(plane); | |
168 | ||
d6f76f37 | 169 | if (plane->fb == NULL) |
51c13278 LP |
170 | return; |
171 | ||
172 | __shmob_drm_plane_setup(splane, plane->fb); | |
173 | } | |
174 | ||
175 | static int | |
176 | shmob_drm_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, | |
177 | struct drm_framebuffer *fb, int crtc_x, int crtc_y, | |
178 | unsigned int crtc_w, unsigned int crtc_h, | |
179 | uint32_t src_x, uint32_t src_y, | |
180 | uint32_t src_w, uint32_t src_h) | |
181 | { | |
182 | struct shmob_drm_plane *splane = to_shmob_plane(plane); | |
183 | struct shmob_drm_device *sdev = plane->dev->dev_private; | |
184 | const struct shmob_drm_format_info *format; | |
185 | ||
186 | format = shmob_drm_format_info(fb->pixel_format); | |
187 | if (format == NULL) { | |
188 | dev_dbg(sdev->dev, "update_plane: unsupported format %08x\n", | |
189 | fb->pixel_format); | |
190 | return -EINVAL; | |
191 | } | |
192 | ||
193 | if (src_w >> 16 != crtc_w || src_h >> 16 != crtc_h) { | |
194 | dev_dbg(sdev->dev, "%s: scaling not supported\n", __func__); | |
195 | return -EINVAL; | |
196 | } | |
197 | ||
198 | splane->format = format; | |
199 | ||
200 | splane->src_x = src_x >> 16; | |
201 | splane->src_y = src_y >> 16; | |
202 | splane->crtc_x = crtc_x; | |
203 | splane->crtc_y = crtc_y; | |
204 | splane->crtc_w = crtc_w; | |
205 | splane->crtc_h = crtc_h; | |
206 | ||
207 | __shmob_drm_plane_setup(splane, fb); | |
208 | return 0; | |
209 | } | |
210 | ||
211 | static int shmob_drm_plane_disable(struct drm_plane *plane) | |
212 | { | |
213 | struct shmob_drm_plane *splane = to_shmob_plane(plane); | |
214 | struct shmob_drm_device *sdev = plane->dev->dev_private; | |
215 | ||
216 | splane->format = NULL; | |
217 | ||
218 | lcdc_write(sdev, LDBnBSIFR(splane->index), 0); | |
219 | return 0; | |
220 | } | |
221 | ||
222 | static void shmob_drm_plane_destroy(struct drm_plane *plane) | |
223 | { | |
51c13278 LP |
224 | shmob_drm_plane_disable(plane); |
225 | drm_plane_cleanup(plane); | |
51c13278 LP |
226 | } |
227 | ||
228 | static const struct drm_plane_funcs shmob_drm_plane_funcs = { | |
229 | .update_plane = shmob_drm_plane_update, | |
230 | .disable_plane = shmob_drm_plane_disable, | |
231 | .destroy = shmob_drm_plane_destroy, | |
232 | }; | |
233 | ||
234 | static const uint32_t formats[] = { | |
235 | DRM_FORMAT_RGB565, | |
236 | DRM_FORMAT_RGB888, | |
237 | DRM_FORMAT_ARGB8888, | |
238 | DRM_FORMAT_NV12, | |
239 | DRM_FORMAT_NV21, | |
240 | DRM_FORMAT_NV16, | |
241 | DRM_FORMAT_NV61, | |
242 | DRM_FORMAT_NV24, | |
243 | DRM_FORMAT_NV42, | |
244 | }; | |
245 | ||
246 | int shmob_drm_plane_create(struct shmob_drm_device *sdev, unsigned int index) | |
247 | { | |
248 | struct shmob_drm_plane *splane; | |
249 | int ret; | |
250 | ||
16ad3b2c | 251 | splane = devm_kzalloc(sdev->dev, sizeof(*splane), GFP_KERNEL); |
51c13278 LP |
252 | if (splane == NULL) |
253 | return -ENOMEM; | |
254 | ||
255 | splane->index = index; | |
256 | splane->alpha = 255; | |
257 | ||
258 | ret = drm_plane_init(sdev->ddev, &splane->plane, 1, | |
259 | &shmob_drm_plane_funcs, formats, | |
260 | ARRAY_SIZE(formats), false); | |
51c13278 LP |
261 | |
262 | return ret; | |
263 | } |