Commit | Line | Data |
---|---|---|
bbbe775e NA |
1 | /* |
2 | * Copyright (C) 2016 BayLibre, SAS | |
3 | * Author: Neil Armstrong <narmstrong@baylibre.com> | |
4 | * Copyright (C) 2015 Amlogic, Inc. All rights reserved. | |
5 | * Copyright (C) 2014 Endless Mobile | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License as | |
9 | * published by the Free Software Foundation; either version 2 of the | |
10 | * License, or (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
19 | * | |
20 | * Written by: | |
21 | * Jasper St. Pierre <jstpierre@mecheye.net> | |
22 | */ | |
23 | ||
24 | #include <linux/kernel.h> | |
25 | #include <linux/module.h> | |
26 | #include <linux/mutex.h> | |
27 | #include <linux/platform_device.h> | |
28 | #include <drm/drmP.h> | |
29 | #include <drm/drm_atomic.h> | |
30 | #include <drm/drm_atomic_helper.h> | |
31 | #include <drm/drm_plane_helper.h> | |
32 | #include <drm/drm_gem_cma_helper.h> | |
33 | #include <drm/drm_fb_cma_helper.h> | |
34 | #include <drm/drm_rect.h> | |
35 | ||
36 | #include "meson_plane.h" | |
37 | #include "meson_vpp.h" | |
38 | #include "meson_viu.h" | |
39 | #include "meson_canvas.h" | |
40 | #include "meson_registers.h" | |
41 | ||
42 | struct meson_plane { | |
43 | struct drm_plane base; | |
44 | struct meson_drm *priv; | |
45 | }; | |
46 | #define to_meson_plane(x) container_of(x, struct meson_plane, base) | |
47 | ||
48 | static int meson_plane_atomic_check(struct drm_plane *plane, | |
49 | struct drm_plane_state *state) | |
50 | { | |
51 | struct drm_crtc_state *crtc_state; | |
52 | struct drm_rect clip = { 0, }; | |
53 | ||
dcafc45d NA |
54 | if (!state->crtc) |
55 | return 0; | |
56 | ||
bbbe775e NA |
57 | crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); |
58 | if (IS_ERR(crtc_state)) | |
59 | return PTR_ERR(crtc_state); | |
60 | ||
61 | clip.x2 = crtc_state->mode.hdisplay; | |
62 | clip.y2 = crtc_state->mode.vdisplay; | |
63 | ||
64 | return drm_plane_helper_check_state(state, &clip, | |
65 | DRM_PLANE_HELPER_NO_SCALING, | |
66 | DRM_PLANE_HELPER_NO_SCALING, | |
67 | true, true); | |
68 | } | |
69 | ||
70 | /* Takes a fixed 16.16 number and converts it to integer. */ | |
71 | static inline int64_t fixed16_to_int(int64_t value) | |
72 | { | |
73 | return value >> 16; | |
74 | } | |
75 | ||
76 | static void meson_plane_atomic_update(struct drm_plane *plane, | |
77 | struct drm_plane_state *old_state) | |
78 | { | |
79 | struct meson_plane *meson_plane = to_meson_plane(plane); | |
80 | struct drm_plane_state *state = plane->state; | |
81 | struct drm_framebuffer *fb = state->fb; | |
82 | struct meson_drm *priv = meson_plane->priv; | |
83 | struct drm_gem_cma_object *gem; | |
84 | struct drm_rect src = { | |
85 | .x1 = (state->src_x), | |
86 | .y1 = (state->src_y), | |
87 | .x2 = (state->src_x + state->src_w), | |
88 | .y2 = (state->src_y + state->src_h), | |
89 | }; | |
90 | struct drm_rect dest = { | |
91 | .x1 = state->crtc_x, | |
92 | .y1 = state->crtc_y, | |
93 | .x2 = state->crtc_x + state->crtc_w, | |
94 | .y2 = state->crtc_y + state->crtc_h, | |
95 | }; | |
96 | unsigned long flags; | |
97 | ||
98 | /* | |
99 | * Update Coordinates | |
100 | * Update Formats | |
101 | * Update Buffer | |
102 | * Enable Plane | |
103 | */ | |
104 | spin_lock_irqsave(&priv->drm->event_lock, flags); | |
105 | ||
106 | /* Enable OSD and BLK0, set max global alpha */ | |
107 | priv->viu.osd1_ctrl_stat = OSD_ENABLE | | |
108 | (0xFF << OSD_GLOBAL_ALPHA_SHIFT) | | |
109 | OSD_BLK0_ENABLE; | |
110 | ||
111 | /* Set up BLK0 to point to the right canvas */ | |
112 | priv->viu.osd1_blk0_cfg[0] = ((MESON_CANVAS_ID_OSD1 << OSD_CANVAS_SEL) | | |
113 | OSD_ENDIANNESS_LE); | |
114 | ||
115 | /* On GXBB, Use the old non-HDR RGB2YUV converter */ | |
116 | if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) | |
117 | priv->viu.osd1_blk0_cfg[0] |= OSD_OUTPUT_COLOR_RGB; | |
118 | ||
119 | switch (fb->pixel_format) { | |
120 | case DRM_FORMAT_XRGB8888: | |
121 | /* For XRGB, replace the pixel's alpha by 0xFF */ | |
122 | writel_bits_relaxed(OSD_REPLACE_EN, OSD_REPLACE_EN, | |
123 | priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); | |
124 | priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_32 | | |
125 | OSD_COLOR_MATRIX_32_ARGB; | |
126 | break; | |
127 | case DRM_FORMAT_ARGB8888: | |
128 | /* For ARGB, use the pixel's alpha */ | |
129 | writel_bits_relaxed(OSD_REPLACE_EN, 0, | |
130 | priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); | |
131 | priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_32 | | |
132 | OSD_COLOR_MATRIX_32_ARGB; | |
133 | break; | |
134 | case DRM_FORMAT_RGB888: | |
135 | priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_24 | | |
136 | OSD_COLOR_MATRIX_24_RGB; | |
137 | break; | |
138 | case DRM_FORMAT_RGB565: | |
139 | priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_16 | | |
140 | OSD_COLOR_MATRIX_16_RGB565; | |
141 | break; | |
142 | }; | |
143 | ||
144 | if (state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) { | |
145 | priv->viu.osd1_interlace = true; | |
146 | ||
147 | dest.y1 /= 2; | |
148 | dest.y2 /= 2; | |
149 | } else | |
150 | priv->viu.osd1_interlace = false; | |
151 | ||
152 | /* | |
153 | * The format of these registers is (x2 << 16 | x1), | |
154 | * where x2 is exclusive. | |
155 | * e.g. +30x1920 would be (1919 << 16) | 30 | |
156 | */ | |
157 | priv->viu.osd1_blk0_cfg[1] = ((fixed16_to_int(src.x2) - 1) << 16) | | |
158 | fixed16_to_int(src.x1); | |
159 | priv->viu.osd1_blk0_cfg[2] = ((fixed16_to_int(src.y2) - 1) << 16) | | |
160 | fixed16_to_int(src.y1); | |
161 | priv->viu.osd1_blk0_cfg[3] = ((dest.x2 - 1) << 16) | dest.x1; | |
162 | priv->viu.osd1_blk0_cfg[4] = ((dest.y2 - 1) << 16) | dest.y1; | |
163 | ||
164 | /* Update Canvas with buffer address */ | |
165 | gem = drm_fb_cma_get_gem_obj(fb, 0); | |
166 | ||
167 | meson_canvas_setup(priv, MESON_CANVAS_ID_OSD1, | |
168 | gem->paddr, fb->pitches[0], | |
169 | fb->height, MESON_CANVAS_WRAP_NONE, | |
170 | MESON_CANVAS_BLKMODE_LINEAR); | |
171 | ||
172 | spin_unlock_irqrestore(&priv->drm->event_lock, flags); | |
173 | } | |
174 | ||
175 | static void meson_plane_atomic_disable(struct drm_plane *plane, | |
176 | struct drm_plane_state *old_state) | |
177 | { | |
178 | struct meson_plane *meson_plane = to_meson_plane(plane); | |
179 | struct meson_drm *priv = meson_plane->priv; | |
180 | ||
181 | /* Disable OSD1 */ | |
182 | writel_bits_relaxed(VPP_OSD1_POSTBLEND, 0, | |
183 | priv->io_base + _REG(VPP_MISC)); | |
184 | ||
185 | } | |
186 | ||
187 | static const struct drm_plane_helper_funcs meson_plane_helper_funcs = { | |
188 | .atomic_check = meson_plane_atomic_check, | |
189 | .atomic_disable = meson_plane_atomic_disable, | |
190 | .atomic_update = meson_plane_atomic_update, | |
191 | }; | |
192 | ||
193 | static const struct drm_plane_funcs meson_plane_funcs = { | |
194 | .update_plane = drm_atomic_helper_update_plane, | |
195 | .disable_plane = drm_atomic_helper_disable_plane, | |
196 | .destroy = drm_plane_cleanup, | |
197 | .reset = drm_atomic_helper_plane_reset, | |
198 | .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, | |
199 | .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, | |
200 | }; | |
201 | ||
202 | static const uint32_t supported_drm_formats[] = { | |
203 | DRM_FORMAT_ARGB8888, | |
204 | DRM_FORMAT_XRGB8888, | |
205 | DRM_FORMAT_RGB888, | |
206 | DRM_FORMAT_RGB565, | |
207 | }; | |
208 | ||
209 | int meson_plane_create(struct meson_drm *priv) | |
210 | { | |
211 | struct meson_plane *meson_plane; | |
212 | struct drm_plane *plane; | |
213 | ||
214 | meson_plane = devm_kzalloc(priv->drm->dev, sizeof(*meson_plane), | |
215 | GFP_KERNEL); | |
216 | if (!meson_plane) | |
217 | return -ENOMEM; | |
218 | ||
219 | meson_plane->priv = priv; | |
220 | plane = &meson_plane->base; | |
221 | ||
222 | drm_universal_plane_init(priv->drm, plane, 0xFF, | |
223 | &meson_plane_funcs, | |
224 | supported_drm_formats, | |
225 | ARRAY_SIZE(supported_drm_formats), | |
226 | DRM_PLANE_TYPE_PRIMARY, "meson_primary_plane"); | |
227 | ||
228 | drm_plane_helper_add(plane, &meson_plane_helper_funcs); | |
229 | ||
230 | priv->primary_plane = plane; | |
231 | ||
232 | return 0; | |
233 | } |