File Index
Files and directories
Sign in
FILE

ast_cursor.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/ast/ast_cursor.c C
  1// SPDX-License-Identifier: MIT
  2/*
  3 * Permission is hereby granted, free of charge, to any person obtaining a
  4 * copy of this software and associated documentation files (the
  5 * "Software"), to deal in the Software without restriction, including
  6 * without limitation the rights to use, copy, modify, merge, publish,
  7 * distribute, sub license, and/or sell copies of the Software, and to
  8 * permit persons to whom the Software is furnished to do so, subject to
  9 * the following conditions:
 10 *
 11 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 12 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 13 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
 14 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
 15 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 16 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 17 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 18 *
 19 * The above copyright notice and this permission notice (including the
 20 * next paragraph) shall be included in all copies or substantial portions
 21 * of the Software.
 22 */
 23
 24#include <linux/bits.h>
 25#include <linux/sizes.h>
 26
 27#include <drm/drm_atomic.h>
 28#include <drm/drm_damage_helper.h>
 29#include <drm/drm_format_helper.h>
 30#include <drm/drm_gem_atomic_helper.h>
 31#include <drm/drm_print.h>
 32
 33#include "ast_drv.h"
 34
 35/*
 36 * Hardware cursor
 37 */
 38
 39/* define for signature structure */
 40#define AST_HWC_SIGNATURE_SIZE		SZ_32
 41#define AST_HWC_SIGNATURE_CHECKSUM	0x00
 42#define AST_HWC_SIGNATURE_SizeX		0x04
 43#define AST_HWC_SIGNATURE_SizeY		0x08
 44#define AST_HWC_SIGNATURE_X		0x0C
 45#define AST_HWC_SIGNATURE_Y		0x10
 46#define AST_HWC_SIGNATURE_HOTSPOTX	0x14
 47#define AST_HWC_SIGNATURE_HOTSPOTY	0x18
 48
 49static unsigned long ast_cursor_vram_size(void)
 50{
 51	return AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE;
 52}
 53
 54long ast_cursor_vram_offset(struct ast_device *ast)
 55{
 56	unsigned long size = ast_cursor_vram_size();
 57
 58	if (size > ast->vram_size)
 59		return -EINVAL;
 60
 61	return ALIGN_DOWN(ast->vram_size - size, SZ_8);
 62}
 63
 64static u32 ast_cursor_calculate_checksum(const void *src, unsigned int width, unsigned int height)
 65{
 66	u32 csum = 0;
 67	unsigned int one_pixel_copy = width & BIT(0);
 68	unsigned int two_pixel_copy = width - one_pixel_copy;
 69	unsigned int trailing_bytes = (AST_MAX_HWC_WIDTH - width) * sizeof(u16);
 70	unsigned int x, y;
 71
 72	for (y = 0; y < height; y++) {
 73		for (x = 0; x < two_pixel_copy; x += 2) {
 74			const u32 *src32 = (const u32 *)src;
 75
 76			csum += *src32;
 77			src += SZ_4;
 78		}
 79		if (one_pixel_copy) {
 80			const u16 *src16 = (const u16 *)src;
 81
 82			csum += *src16;
 83			src += SZ_2;
 84		}
 85		src += trailing_bytes;
 86	}
 87
 88	return csum;
 89}
 90
 91static void ast_set_cursor_image(struct ast_device *ast, const u8 *src,
 92				 unsigned int width, unsigned int height)
 93{
 94	u8 __iomem *dst = ast_plane_vaddr(&ast->cursor_plane.base);
 95	u32 csum = ast_cursor_calculate_checksum(src, width, height);
 96
 97	/* write pixel data */
 98#if defined(__BIG_ENDIAN)
 99	unsigned int i;
100
101	for (i = 0; i < AST_HWC_SIZE; i += 2)
102		writew(swab16(*(const __u16 *)&src[i]), &dst[i]);
103#else
104	memcpy_toio(dst, src, AST_HWC_SIZE);
105#endif
106
107	/* write checksum + signature */
108	dst += AST_HWC_SIZE;
109	writel(csum, dst);
110	writel(width, dst + AST_HWC_SIGNATURE_SizeX);
111	writel(height, dst + AST_HWC_SIGNATURE_SizeY);
112	writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTX);
113	writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTY);
114}
115
116static void ast_set_cursor_base(struct ast_device *ast, u64 address)
117{
118	u8 addr0 = (address >> 3) & 0xff;
119	u8 addr1 = (address >> 11) & 0xff;
120	u8 addr2 = (address >> 19) & 0xff;
121
122	ast_set_index_reg(ast, AST_IO_VGACRI, 0xc8, addr0);
123	ast_set_index_reg(ast, AST_IO_VGACRI, 0xc9, addr1);
124	ast_set_index_reg(ast, AST_IO_VGACRI, 0xca, addr2);
125}
126
127static void ast_set_cursor_location(struct ast_device *ast, u16 x, u16 y,
128				    u8 x_offset, u8 y_offset)
129{
130	u8 x0 = (x & 0x00ff);
131	u8 x1 = (x & 0x0f00) >> 8;
132	u8 y0 = (y & 0x00ff);
133	u8 y1 = (y & 0x0700) >> 8;
134
135	ast_set_index_reg(ast, AST_IO_VGACRI, 0xc2, x_offset);
136	ast_set_index_reg(ast, AST_IO_VGACRI, 0xc3, y_offset);
137	ast_set_index_reg(ast, AST_IO_VGACRI, 0xc4, x0);
138	ast_set_index_reg(ast, AST_IO_VGACRI, 0xc5, x1);
139	ast_set_index_reg(ast, AST_IO_VGACRI, 0xc6, y0);
140	ast_set_index_reg(ast, AST_IO_VGACRI, 0xc7, y1);
141}
142
143static void ast_set_cursor_enabled(struct ast_device *ast, bool enabled)
144{
145	static const u8 mask = (u8)~(AST_IO_VGACRCB_HWC_16BPP |
146				     AST_IO_VGACRCB_HWC_ENABLED);
147
148	u8 vgacrcb = AST_IO_VGACRCB_HWC_16BPP;
149
150	if (enabled)
151		vgacrcb |= AST_IO_VGACRCB_HWC_ENABLED;
152
153	ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xcb, mask, vgacrcb);
154}
155
156/*
157 * Cursor plane
158 */
159
160static const uint32_t ast_cursor_plane_formats[] = {
161	DRM_FORMAT_ARGB4444,
162	DRM_FORMAT_ARGB8888,
163};
164
165static int ast_cursor_plane_helper_atomic_check(struct drm_plane *plane,
166						struct drm_atomic_state *state)
167{
168	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
169	struct drm_framebuffer *new_fb = new_plane_state->fb;
170	struct drm_crtc_state *new_crtc_state = NULL;
171	int ret;
172
173	if (new_plane_state->crtc)
174		new_crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_state->crtc);
175
176	ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
177						  DRM_PLANE_NO_SCALING,
178						  DRM_PLANE_NO_SCALING,
179						  true, true);
180	if (ret || !new_plane_state->visible)
181		return ret;
182
183	if (new_fb->width > AST_MAX_HWC_WIDTH || new_fb->height > AST_MAX_HWC_HEIGHT)
184		return -EINVAL;
185
186	return 0;
187}
188
189static void ast_cursor_plane_helper_atomic_update(struct drm_plane *plane,
190						  struct drm_atomic_state *state)
191{
192	struct ast_cursor_plane *ast_cursor_plane = to_ast_cursor_plane(plane);
193	struct ast_plane *ast_plane = to_ast_plane(plane);
194	struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
195	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
196	struct drm_framebuffer *fb = plane_state->fb;
197	struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
198	struct ast_device *ast = to_ast_device(plane->dev);
199	struct drm_rect damage;
200	u64 dst_off = ast_plane->offset;
201	u8 __iomem *dst = ast_plane_vaddr(ast_plane); /* TODO: Use mapping abstraction properly */
202	u8 __iomem *sig = dst + AST_HWC_SIZE; /* TODO: Use mapping abstraction properly */
203	unsigned int offset_x, offset_y;
204	u16 x, y;
205	u8 x_offset, y_offset;
206
207	/*
208	 * Do data transfer to hardware buffer and point the scanout
209	 * engine to the offset.
210	 */
211
212	if (drm_atomic_helper_damage_merged(old_plane_state, plane_state, &damage)) {
213		u8 *argb4444;
214
215		switch (fb->format->format) {
216		case DRM_FORMAT_ARGB4444:
217			argb4444 = shadow_plane_state->data[0].vaddr;
218			break;
219		default:
220			argb4444 = ast_cursor_plane->argb4444;
221			{
222				struct iosys_map argb4444_dst[DRM_FORMAT_MAX_PLANES] = {
223					IOSYS_MAP_INIT_VADDR(argb4444),
224				};
225				unsigned int argb4444_dst_pitch[DRM_FORMAT_MAX_PLANES] = {
226					AST_HWC_PITCH,
227				};
228
229				drm_fb_argb8888_to_argb4444(argb4444_dst, argb4444_dst_pitch,
230							    shadow_plane_state->data, fb, &damage,
231							    &shadow_plane_state->fmtcnv_state);
232			}
233			break;
234		}
235		ast_set_cursor_image(ast, argb4444, fb->width, fb->height);
236		ast_set_cursor_base(ast, dst_off);
237	}
238
239	/*
240	 * Update location in HWC signature and registers.
241	 */
242
243	writel(plane_state->crtc_x, sig + AST_HWC_SIGNATURE_X);
244	writel(plane_state->crtc_y, sig + AST_HWC_SIGNATURE_Y);
245
246	offset_x = AST_MAX_HWC_WIDTH - fb->width;
247	offset_y = AST_MAX_HWC_HEIGHT - fb->height;
248
249	if (plane_state->crtc_x < 0) {
250		x_offset = (-plane_state->crtc_x) + offset_x;
251		x = 0;
252	} else {
253		x_offset = offset_x;
254		x = plane_state->crtc_x;
255	}
256	if (plane_state->crtc_y < 0) {
257		y_offset = (-plane_state->crtc_y) + offset_y;
258		y = 0;
259	} else {
260		y_offset = offset_y;
261		y = plane_state->crtc_y;
262	}
263
264	ast_set_cursor_location(ast, x, y, x_offset, y_offset);
265
266	/* Dummy write to enable HWC and make the HW pick-up the changes. */
267	ast_set_cursor_enabled(ast, true);
268}
269
270static void ast_cursor_plane_helper_atomic_disable(struct drm_plane *plane,
271						   struct drm_atomic_state *state)
272{
273	struct ast_device *ast = to_ast_device(plane->dev);
274
275	ast_set_cursor_enabled(ast, false);
276}
277
278static const struct drm_plane_helper_funcs ast_cursor_plane_helper_funcs = {
279	DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
280	.atomic_check = ast_cursor_plane_helper_atomic_check,
281	.atomic_update = ast_cursor_plane_helper_atomic_update,
282	.atomic_disable = ast_cursor_plane_helper_atomic_disable,
283};
284
285static const struct drm_plane_funcs ast_cursor_plane_funcs = {
286	.update_plane = drm_atomic_helper_update_plane,
287	.disable_plane = drm_atomic_helper_disable_plane,
288	.destroy = drm_plane_cleanup,
289	DRM_GEM_SHADOW_PLANE_FUNCS,
290};
291
292int ast_cursor_plane_init(struct ast_device *ast)
293{
294	struct drm_device *dev = &ast->base;
295	struct ast_cursor_plane *ast_cursor_plane = &ast->cursor_plane;
296	struct ast_plane *ast_plane = &ast_cursor_plane->base;
297	struct drm_plane *cursor_plane = &ast_plane->base;
298	unsigned long size;
299	long offset;
300	int ret;
301
302	size = ast_cursor_vram_size();
303	offset = ast_cursor_vram_offset(ast);
304	if (offset < 0)
305		return offset;
306
307	ret = ast_plane_init(dev, ast_plane, offset, size,
308			     0x01, &ast_cursor_plane_funcs,
309			     ast_cursor_plane_formats, ARRAY_SIZE(ast_cursor_plane_formats),
310			     NULL, DRM_PLANE_TYPE_CURSOR);
311	if (ret) {
312		drm_err(dev, "ast_plane_init() failed: %d\n", ret);
313		return ret;
314	}
315	drm_plane_helper_add(cursor_plane, &ast_cursor_plane_helper_funcs);
316	drm_plane_enable_fb_damage_clips(cursor_plane);
317
318	return 0;
319}
~/ Read-only file preview.