blob: 2868909aa926de5a85d302972fe5d1839866485b [file] [log] [blame]
Benjamin Gaignard96006a72014-12-11 13:34:42 +01001/*
2 * Copyright (C) STMicroelectronics SA 2014
3 * Authors: Vincent Abriou <vincent.abriou@st.com>
4 * Fabien Dessenne <fabien.dessenne@st.com>
5 * for STMicroelectronics.
6 * License terms: GNU General Public License (GPL), version 2
7 */
8#include <drm/drmP.h>
9
10#include "sti_cursor.h"
Vincent Abriou9e1f05b2015-07-31 11:32:34 +020011#include "sti_plane.h"
Benjamin Gaignard96006a72014-12-11 13:34:42 +010012#include "sti_vtg.h"
13
14/* Registers */
15#define CUR_CTL 0x00
16#define CUR_VPO 0x0C
17#define CUR_PML 0x14
18#define CUR_PMP 0x18
19#define CUR_SIZE 0x1C
20#define CUR_CML 0x20
21#define CUR_AWS 0x28
22#define CUR_AWE 0x2C
23
24#define CUR_CTL_CLUT_UPDATE BIT(1)
25
26#define STI_CURS_MIN_SIZE 1
27#define STI_CURS_MAX_SIZE 128
28
29/*
30 * pixmap dma buffer stucture
31 *
32 * @paddr: physical address
33 * @size: buffer size
34 * @base: virtual address
35 */
36struct dma_pixmap {
37 dma_addr_t paddr;
38 size_t size;
39 void *base;
40};
41
42/**
43 * STI Cursor structure
44 *
Vincent Abriou871bcdf2015-07-31 11:32:13 +020045 * @sti_plane: sti_plane structure
46 * @dev: driver device
47 * @regs: cursor registers
Benjamin Gaignard96006a72014-12-11 13:34:42 +010048 * @width: cursor width
49 * @height: cursor height
50 * @clut: color look up table
51 * @clut_paddr: color look up table physical address
52 * @pixmap: pixmap dma buffer (clut8-format cursor)
53 */
54struct sti_cursor {
Vincent Abriou871bcdf2015-07-31 11:32:13 +020055 struct sti_plane plane;
56 struct device *dev;
57 void __iomem *regs;
Benjamin Gaignard96006a72014-12-11 13:34:42 +010058 unsigned int width;
59 unsigned int height;
60 unsigned short *clut;
61 dma_addr_t clut_paddr;
62 struct dma_pixmap pixmap;
63};
64
65static const uint32_t cursor_supported_formats[] = {
66 DRM_FORMAT_ARGB8888,
67};
68
Vincent Abriou871bcdf2015-07-31 11:32:13 +020069#define to_sti_cursor(x) container_of(x, struct sti_cursor, plane)
Benjamin Gaignard96006a72014-12-11 13:34:42 +010070
Vincent Abriou871bcdf2015-07-31 11:32:13 +020071static const uint32_t *sti_cursor_get_formats(struct sti_plane *plane)
Benjamin Gaignard96006a72014-12-11 13:34:42 +010072{
73 return cursor_supported_formats;
74}
75
Vincent Abriou871bcdf2015-07-31 11:32:13 +020076static unsigned int sti_cursor_get_nb_formats(struct sti_plane *plane)
Benjamin Gaignard96006a72014-12-11 13:34:42 +010077{
78 return ARRAY_SIZE(cursor_supported_formats);
79}
80
Vincent Abriou871bcdf2015-07-31 11:32:13 +020081static void sti_cursor_argb8888_to_clut8(struct sti_plane *plane)
Benjamin Gaignard96006a72014-12-11 13:34:42 +010082{
Vincent Abriou871bcdf2015-07-31 11:32:13 +020083 struct sti_cursor *cursor = to_sti_cursor(plane);
84 u32 *src = plane->vaddr;
Benjamin Gaignard96006a72014-12-11 13:34:42 +010085 u8 *dst = cursor->pixmap.base;
86 unsigned int i, j;
87 u32 a, r, g, b;
88
89 for (i = 0; i < cursor->height; i++) {
90 for (j = 0; j < cursor->width; j++) {
91 /* Pick the 2 higher bits of each component */
92 a = (*src >> 30) & 3;
93 r = (*src >> 22) & 3;
94 g = (*src >> 14) & 3;
95 b = (*src >> 6) & 3;
96 *dst = a << 6 | r << 4 | g << 2 | b;
97 src++;
98 dst++;
99 }
100 }
101}
102
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200103static int sti_cursor_prepare_plane(struct sti_plane *plane, bool first_prepare)
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100104{
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200105 struct sti_cursor *cursor = to_sti_cursor(plane);
106 struct drm_display_mode *mode = plane->mode;
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100107 u32 y, x;
108 u32 val;
109
110 DRM_DEBUG_DRIVER("\n");
111
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200112 dev_dbg(cursor->dev, "%s %s\n", __func__, sti_plane_to_str(plane));
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100113
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200114 if (plane->src_w < STI_CURS_MIN_SIZE ||
115 plane->src_h < STI_CURS_MIN_SIZE ||
116 plane->src_w > STI_CURS_MAX_SIZE ||
117 plane->src_h > STI_CURS_MAX_SIZE) {
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100118 DRM_ERROR("Invalid cursor size (%dx%d)\n",
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200119 plane->src_w, plane->src_h);
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100120 return -EINVAL;
121 }
122
123 /* If the cursor size has changed, re-allocated the pixmap */
124 if (!cursor->pixmap.base ||
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200125 (cursor->width != plane->src_w) ||
126 (cursor->height != plane->src_h)) {
127 cursor->width = plane->src_w;
128 cursor->height = plane->src_h;
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100129
130 if (cursor->pixmap.base)
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200131 dma_free_writecombine(cursor->dev,
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100132 cursor->pixmap.size,
133 cursor->pixmap.base,
134 cursor->pixmap.paddr);
135
136 cursor->pixmap.size = cursor->width * cursor->height;
137
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200138 cursor->pixmap.base = dma_alloc_writecombine(cursor->dev,
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100139 cursor->pixmap.size,
140 &cursor->pixmap.paddr,
141 GFP_KERNEL | GFP_DMA);
142 if (!cursor->pixmap.base) {
143 DRM_ERROR("Failed to allocate memory for pixmap\n");
144 return -ENOMEM;
145 }
146 }
147
148 /* Convert ARGB8888 to CLUT8 */
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200149 sti_cursor_argb8888_to_clut8(plane);
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100150
151 /* AWS and AWE depend on the mode */
152 y = sti_vtg_get_line_number(*mode, 0);
153 x = sti_vtg_get_pixel_number(*mode, 0);
154 val = y << 16 | x;
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200155 writel(val, cursor->regs + CUR_AWS);
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100156 y = sti_vtg_get_line_number(*mode, mode->vdisplay - 1);
157 x = sti_vtg_get_pixel_number(*mode, mode->hdisplay - 1);
158 val = y << 16 | x;
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200159 writel(val, cursor->regs + CUR_AWE);
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100160
161 if (first_prepare) {
162 /* Set and fetch CLUT */
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200163 writel(cursor->clut_paddr, cursor->regs + CUR_CML);
164 writel(CUR_CTL_CLUT_UPDATE, cursor->regs + CUR_CTL);
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100165 }
166
167 return 0;
168}
169
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200170static int sti_cursor_commit_plane(struct sti_plane *plane)
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100171{
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200172 struct sti_cursor *cursor = to_sti_cursor(plane);
173 struct drm_display_mode *mode = plane->mode;
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100174 u32 ydo, xdo;
175
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200176 dev_dbg(cursor->dev, "%s %s\n", __func__, sti_plane_to_str(plane));
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100177
178 /* Set memory location, size, and position */
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200179 writel(cursor->pixmap.paddr, cursor->regs + CUR_PML);
180 writel(cursor->width, cursor->regs + CUR_PMP);
181 writel(cursor->height << 16 | cursor->width, cursor->regs + CUR_SIZE);
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100182
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200183 ydo = sti_vtg_get_line_number(*mode, plane->dst_y);
184 xdo = sti_vtg_get_pixel_number(*mode, plane->dst_y);
185 writel((ydo << 16) | xdo, cursor->regs + CUR_VPO);
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100186
187 return 0;
188}
189
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200190static int sti_cursor_disable_plane(struct sti_plane *plane)
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100191{
192 return 0;
193}
194
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200195static void sti_cursor_init(struct sti_cursor *cursor)
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100196{
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100197 unsigned short *base = cursor->clut;
198 unsigned int a, r, g, b;
199
200 /* Assign CLUT values, ARGB444 format */
201 for (a = 0; a < 4; a++)
202 for (r = 0; r < 4; r++)
203 for (g = 0; g < 4; g++)
204 for (b = 0; b < 4; b++)
205 *base++ = (a * 5) << 12 |
206 (r * 5) << 8 |
207 (g * 5) << 4 |
208 (b * 5);
209}
210
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200211static const struct sti_plane_funcs cursor_plane_ops = {
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100212 .get_formats = sti_cursor_get_formats,
213 .get_nb_formats = sti_cursor_get_nb_formats,
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200214 .prepare = sti_cursor_prepare_plane,
215 .commit = sti_cursor_commit_plane,
216 .disable = sti_cursor_disable_plane,
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100217};
218
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200219struct sti_plane *sti_cursor_create(struct device *dev, int desc,
220 void __iomem *baseaddr)
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100221{
222 struct sti_cursor *cursor;
223
224 cursor = devm_kzalloc(dev, sizeof(*cursor), GFP_KERNEL);
225 if (!cursor) {
226 DRM_ERROR("Failed to allocate memory for cursor\n");
227 return NULL;
228 }
229
230 /* Allocate clut buffer */
231 cursor->clut = dma_alloc_writecombine(dev,
232 0x100 * sizeof(unsigned short),
233 &cursor->clut_paddr,
234 GFP_KERNEL | GFP_DMA);
235
236 if (!cursor->clut) {
237 DRM_ERROR("Failed to allocate memory for cursor clut\n");
238 devm_kfree(dev, cursor);
239 return NULL;
240 }
241
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200242 cursor->dev = dev;
243 cursor->regs = baseaddr;
244 cursor->plane.desc = desc;
245 cursor->plane.ops = &cursor_plane_ops;
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100246
Vincent Abriou871bcdf2015-07-31 11:32:13 +0200247 sti_cursor_init(cursor);
248
249 return &cursor->plane;
Benjamin Gaignard96006a72014-12-11 13:34:42 +0100250}