1 /*
2 * Copyright (C) 2019 Allwinnertech Co.Ltd
3 * Authors: zhengwanyu
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 */
11 #include <drm/drm_crtc_helper.h>
12 #include <drm/drm_fb_helper.h>
13 #include <drm/drm_fourcc.h>
14
15 #include "sunxi_drm_bootlogo.h"
16 #include "sunxi_drm_gem.h"
17 #include "sunxi_drm_fb.h"
18 #include "sunxi_drm_fbdev.h"
19
20 #include <linux/memblock.h>
21
22 #ifndef MODULE
23 static u32 disp_reserve_size;
24 static u32 disp_reserve_base;
disp_reserve_mem(bool reserve)25 int disp_reserve_mem(bool reserve)
26 {
27 if (!disp_reserve_size || !disp_reserve_base)
28 return -EINVAL;
29
30 if (reserve)
31 memblock_reserve(disp_reserve_base, disp_reserve_size);
32 else
33 memblock_free(disp_reserve_base, disp_reserve_size);
34
35 return 0;
36 }
37
early_disp_reserve(char * str)38 static int __init early_disp_reserve(char *str)
39 {
40 u32 temp[3] = {0};
41
42 if (!str)
43 return -EINVAL;
44
45 get_options(str, 3, temp);
46
47 disp_reserve_size = temp[1];
48 disp_reserve_base = temp[2];
49 if (temp[0] != 2 || disp_reserve_size <= 0)
50 return -EINVAL;
51
52 /*arch_mem_end = memblock_end_of_DRAM();*/
53 disp_reserve_mem(true);
54 pr_info("disp reserve base 0x%x ,size 0x%x\n",
55 disp_reserve_base, disp_reserve_size);
56 return 0;
57 }
58 early_param("disp_reserve", early_disp_reserve);
59 #endif
60
sunxi_drm_map_kernel(unsigned long phys_addr,unsigned long size)61 static void *sunxi_drm_map_kernel(unsigned long phys_addr,
62 unsigned long size)
63 {
64 int npages = PAGE_ALIGN(size) / PAGE_SIZE;
65 struct page **pages = vmalloc(sizeof(struct page *) * npages);
66 struct page **tmp = pages;
67 struct page *cur_page = phys_to_page(phys_addr);
68 pgprot_t pgprot;
69 void *vaddr = NULL;
70 int i;
71
72 if (!pages)
73 return NULL;
74
75 for (i = 0; i < npages; i++)
76 *(tmp++) = cur_page++;
77
78 pgprot = pgprot_noncached(PAGE_KERNEL);
79 vaddr = vmap(pages, npages, VM_MAP, pgprot);
80
81 vfree(pages);
82 return vaddr;
83 }
84
sunxi_drm_unmap_kernel(void * vaddr)85 static void sunxi_drm_unmap_kernel(void *vaddr)
86 {
87 vunmap(vaddr);
88 }
89
Fb_copy_boot_fb(char * fb_vaddr,struct bootlogo_info * bootlogo,unsigned int fb_w,unsigned int fb_h,unsigned int fb_bpp,unsigned int fb_stride)90 static int Fb_copy_boot_fb(char *fb_vaddr, struct bootlogo_info *bootlogo,
91 unsigned int fb_w, unsigned int fb_h, unsigned int fb_bpp, unsigned int fb_stride)
92 {
93 enum {
94 BOOT_FB_ADDR = 0,
95 BOOT_FB_WIDTH,
96 BOOT_FB_HEIGHT,
97 BOOT_FB_BPP,
98 BOOT_FB_STRIDE,
99 BOOT_FB_CROP_L,
100 BOOT_FB_CROP_T,
101 BOOT_FB_CROP_R,
102 BOOT_FB_CROP_B,
103 };
104
105 unsigned int src_phy_addr = 0;
106 char *src_addr = NULL;
107 char *src_addr_b = NULL;
108 char *src_addr_e = NULL;
109 int src_width = 0;
110 int src_height = 0;
111 int src_bpp = 0;
112 int src_stride = 0;
113 int src_cp_btyes = 0;
114 int src_crop_l = 0;
115 int src_crop_t = 0;
116 int src_crop_r = 0;
117 int src_crop_b = 0;
118
119 char *dst_addr = NULL;
120 int dst_width = 0;
121 int dst_height = 0;
122 int dst_bpp = 0;
123 int dst_stride = 0;
124
125 unsigned long map_offset;
126
127 src_phy_addr = bootlogo->phy_addr;
128 src_width = bootlogo->width;
129 src_height = bootlogo->height;
130 src_bpp = bootlogo->bpp;
131 src_stride = bootlogo->stride;
132 src_crop_l = bootlogo->left;
133 src_crop_t = bootlogo->top;
134 src_crop_r = bootlogo->right;
135 src_crop_b = bootlogo->bottom;
136
137 dst_addr = fb_vaddr;
138 dst_width = fb_w;
139 dst_height = fb_h;
140 dst_bpp = fb_bpp;
141 dst_stride = fb_stride;
142
143 if ((src_phy_addr == 0)
144 || (src_width <= 0)
145 || (src_height <= 0)
146 || (src_stride <= 0)
147 || (src_bpp <= 0)
148 || (dst_addr == NULL)
149 || (dst_width <= 0)
150 || (dst_height <= 0)
151 || (dst_stride <= 0)
152 || (dst_bpp <= 0)
153 || (src_bpp != dst_bpp)) {
154 DRM_ERROR
155 ("wrong para: src[phy_addr=%d,w=%d,h=%d,bpp=%d,stride=%d], dst[addr=%p,w=%d,h=%d,bpp=%d,stride=%d]\n",
156 src_phy_addr,
157 src_width, src_height, src_bpp, src_stride, dst_addr,
158 dst_width, dst_height, dst_bpp, dst_stride);
159 return -1;
160 }
161
162 map_offset = (unsigned long)src_phy_addr + PAGE_SIZE
163 - PAGE_ALIGN((unsigned long)src_phy_addr + 1);
164 src_addr = (char *)sunxi_drm_map_kernel((unsigned long)src_phy_addr -
165 map_offset,
166 src_stride * src_height +
167 map_offset);
168 if (src_addr == NULL) {
169 DRM_ERROR("Fb_map_kernel_cache for src_addr failed\n");
170 return -1;
171 }
172
173 src_addr_b = src_addr + map_offset;
174 if ((src_crop_b > src_crop_t) &&
175 (src_height > src_crop_b - src_crop_t) &&
176 (src_crop_t >= 0) &&
177 (src_height >= src_crop_b)) {
178 src_height = src_crop_b - src_crop_t;
179 src_addr_b += (src_stride * src_crop_t);
180 }
181 if ((src_crop_r > src_crop_l)
182 && (src_width > src_crop_r - src_crop_l)
183 && (src_crop_l >= 0)
184 && (src_width >= src_crop_r)) {
185 src_width = src_crop_r - src_crop_l;
186 src_addr_b += (src_crop_l * src_bpp >> 3);
187 }
188 if (src_height < dst_height) {
189 int dst_crop_t = (dst_height - src_height) >> 1;
190
191 dst_addr += (dst_stride * dst_crop_t);
192 } else if (src_height > dst_height) {
193 DRM_ERROR("src_height(%d) > dst_height(%d),please cut the height\n",
194 src_height,
195 dst_height);
196 sunxi_drm_unmap_kernel(src_addr);
197 return -1;
198 }
199 if (src_width < dst_width) {
200 int dst_crop_l = (dst_width - src_width) >> 1;
201
202 dst_addr += (dst_crop_l * dst_bpp >> 3);
203 } else if (src_width > dst_width) {
204 DRM_ERROR("src_width(%d) > dst_width(%d),please cut the width!\n",
205 src_width,
206 dst_width);
207 sunxi_drm_unmap_kernel(src_addr);
208 return -1;
209 }
210
211 src_cp_btyes = src_width * src_bpp >> 3;
212 src_addr_e = src_addr_b + src_stride * src_height;
213 for (; src_addr_b != src_addr_e; src_addr_b += src_stride) {
214 memcpy((void *)dst_addr, (void *)src_addr_b, src_cp_btyes);
215 dst_addr += dst_stride;
216 }
217
218 sunxi_drm_unmap_kernel(src_addr);
219
220 #ifndef MODULE
221 disp_reserve_mem(false);
222 #endif
223
224 return 0;
225 }
226
sunxi_drm_get_bootlogoinfo_from_dts_info(struct bootlogo_info * bootlogo,char * dts_attr)227 void sunxi_drm_get_bootlogoinfo_from_dts_info(
228 struct bootlogo_info *bootlogo, char *dts_attr)
229 {
230 char *end = dts_attr;
231 unsigned long long value;
232
233 bootlogo->pics_format = BOOTLOGO_FORMAT_OTHERS;
234
235 value = simple_strtoull(end, &end, 16);
236 bootlogo->phy_addr = (unsigned int)value;
237 if ((*end != ' ') && (*end != ',')) {
238 DRM_ERROR("error separator:%c\n", *end);
239 return;
240 }
241
242 value = simple_strtoull(end + 1, &end, 16);
243 bootlogo->width = (unsigned int)value;
244 if ((*end != ' ') && (*end != ',')) {
245 DRM_ERROR("error separator:%c\n", *end);
246 return;
247 }
248
249 value = simple_strtoull(end + 1, &end, 16);
250 bootlogo->height = (unsigned int)value;
251 if ((*end != ' ') && (*end != ',')) {
252 DRM_ERROR("error separator:%c\n", *end);
253 return;
254 }
255
256 value = simple_strtoull(end + 1, &end, 16);
257 bootlogo->bpp = (unsigned int)value;
258 if ((*end != ' ') && (*end != ',')) {
259 DRM_ERROR("error separator:%c\n", *end);
260 return;
261 }
262
263 value = simple_strtoull(end + 1, &end, 16);
264 bootlogo->stride = (unsigned int)value;
265 if ((*end != ' ') && (*end != ',')) {
266 DRM_ERROR("error separator:%c\n", *end);
267 return;
268 }
269
270 value = simple_strtoull(end + 1, &end, 16);
271 bootlogo->left = (unsigned int)value;
272 if ((*end != ' ') && (*end != ',')) {
273 DRM_ERROR("error separator:%c\n", *end);
274 return;
275 }
276
277 value = simple_strtoull(end + 1, &end, 16);
278 bootlogo->top = (unsigned int)value;
279 if ((*end != ' ') && (*end != ',')) {
280 DRM_ERROR("error separator:%c\n", *end);
281 return;
282 }
283
284 value = simple_strtoull(end + 1, &end, 16);
285 bootlogo->right = (unsigned int)value;
286 if ((*end != ' ') && (*end != ',')) {
287 DRM_ERROR("error separator:%c\n", *end);
288 return;
289 }
290
291 value = simple_strtoull(end + 1, &end, 16);
292 bootlogo->bottom = (unsigned int)value;
293
294 DRM_INFO("phyaddr:0x%x w-h:%ux%u bpp:%u stride:%u crop:(%u, %u)-(%u, %u)\n",
295 bootlogo->phy_addr, bootlogo->width, bootlogo->height,
296 bootlogo->bpp, bootlogo->stride,
297 bootlogo->left, bootlogo->top, bootlogo->right, bootlogo->bottom);
298
299 return;
300 }
301
sunxi_drm_get_bootlogoinfo_from_bmp_header(struct bootlogo_info * bootlogo,struct bmp_header * header,unsigned int fb_base)302 void sunxi_drm_get_bootlogoinfo_from_bmp_header(
303 struct bootlogo_info *bootlogo, struct bmp_header *header,
304 unsigned int fb_base)
305 {
306 bootlogo->pics_format = BOOTLOGO_FORMAT_BMP;
307
308 bootlogo->phy_addr = fb_base + header->data_offset;
309 bootlogo->width = header->width;
310 bootlogo->height = header->height;
311 bootlogo->bpp = header->bit_count;
312 bootlogo->stride = header->width * (header->bit_count / 8);
313 bootlogo->left = 0;
314 bootlogo->top = 0;
315 bootlogo->right = header->width;
316 bootlogo->bottom = (header->height & 0x80000000) ?
317 (-header->height) : (header->height);
318 }
319
320 /*get the header of bmp and parse it to check if it is good*/
sunxi_drm_parse_bmp_header(struct bmp_header * header,unsigned long phy_addr)321 int sunxi_drm_parse_bmp_header(struct bmp_header *header,
322 unsigned long phy_addr)
323 {
324 unsigned int bytes_per_pixel;
325 struct bmp_header *tmp_header;
326
327 tmp_header = sunxi_drm_map_kernel(phy_addr, sizeof(struct bmp_header));
328 if (!tmp_header) {
329 DRM_ERROR("map bmp header failed\n");
330 return -1;
331 }
332
333 if ((tmp_header->signature[0] != 'B') || (tmp_header->signature[1] != 'M')) {
334 DRM_ERROR("This is NOT a bmp picture, signature:%c %c\n",
335 tmp_header->signature[0], tmp_header->signature[1]);
336 return -1;
337 }
338
339 bytes_per_pixel = tmp_header->bit_count / 8;
340 if ((bytes_per_pixel != 3) && (bytes_per_pixel != 4)) {
341 DRM_ERROR("error bmp bit counts per pixel:%d\n", tmp_header->bit_count);
342 return -1;
343 }
344 DRM_INFO("bmp file, w:%d h:%d\n", tmp_header->width, tmp_header->height);
345
346 memcpy(header, tmp_header, sizeof(struct bmp_header));
347
348 sunxi_drm_unmap_kernel(tmp_header);
349 return 0;
350 }
351
sunxi_drm_copy_bootlogo(unsigned char * d_addr,unsigned int w,unsigned int h,struct bootlogo_info * bootlogo)352 static int sunxi_drm_copy_bootlogo(unsigned char *d_addr, unsigned int w,
353 unsigned int h, struct bootlogo_info *bootlogo)
354 {
355 unsigned char *src_addr;
356 unsigned int i;
357 unsigned int x, y;
358 unsigned int effective_width, effective_height;
359 void *screen_offset = NULL;
360 unsigned int bytes_per_pixel;
361
362 if (bootlogo->height & 0x80000000)
363 src_addr = sunxi_drm_map_kernel(bootlogo->phy_addr,
364 (-bootlogo->height) * bootlogo->stride);
365 else
366 src_addr = sunxi_drm_map_kernel(bootlogo->phy_addr,
367 bootlogo->height * bootlogo->stride);
368
369 if (!src_addr) {
370 DRM_ERROR("sunxi_drm_map_kernel failed\n");
371 return -1;
372 }
373
374 x = bootlogo->width;
375 y = (bootlogo->height & 0x80000000) ?
376 (-bootlogo->height) : (bootlogo->height);
377
378 effective_width = (w < x) ? w : x;
379 effective_height = (h < y) ? h : y;
380
381 bytes_per_pixel = bootlogo->bpp >> 3;
382
383 DRM_INFO("x-y:%dx%d w-h:%dx%d effective:%dx%d bytes_per_pixel:%d\n",
384 x, y, w, h, effective_width, effective_height, bytes_per_pixel);
385
386 src_addr += PAGE_SIZE;
387
388 /* if the param 'height' in bmp file is negative, it means that
389 * the data is in normal sequence, or it is inverse sequence that the first line
390 * normal is the last line in bmp file(mirror)
391 */
392 if (((bootlogo->pics_format == BOOTLOGO_FORMAT_BMP) && (bootlogo->height & 0x80000000))
393 || (bootlogo->pics_format == BOOTLOGO_FORMAT_OTHERS)) {
394 DRM_INFO("%s Start Copy bootlogo data:\n", __func__);
395 screen_offset =
396 (void *)((void *__force)d_addr
397 + (w * (abs(h - y) / 2) + abs(w - x) / 2)
398 * bytes_per_pixel);
399
400 for (i = 0; i < effective_height; i++) {
401 memcpy((void *)screen_offset, src_addr, effective_width * bytes_per_pixel);
402 screen_offset = (void *)(screen_offset + w * bytes_per_pixel);
403 src_addr = (void *)src_addr + x * bytes_per_pixel;
404 }
405 } else {
406 screen_offset =
407 (void *)((void *__force)d_addr +
408 (w * (abs(h - y) / 2) + abs(w - x) / 2)
409 * bytes_per_pixel);
410 src_addr =
411 (void *)(src_addr + (x * (abs(y - h) / 2)
412 + abs(x - w) / 2) * bytes_per_pixel);
413
414 src_addr =
415 (void *)src_addr + (effective_height - 1) * x * bytes_per_pixel;
416 for (i = effective_height - 1; i >= 0; i--) {
417 memcpy((void *)screen_offset, src_addr, effective_width * bytes_per_pixel);
418 screen_offset =
419 (void *)(screen_offset + w * bytes_per_pixel);
420 src_addr =
421 (void *)src_addr + i * x * bytes_per_pixel;
422 }
423 }
424
425 sunxi_drm_unmap_kernel(src_addr);
426
427 return 0;
428 }
429
430
sunxi_drm_fbdev_copy_bootlogo(struct drm_device * dev,struct bootlogo_info * bootlogo)431 int sunxi_drm_fbdev_copy_bootlogo(
432 struct drm_device *dev, struct bootlogo_info *bootlogo)
433 {
434 unsigned char *fbdev_addr;
435
436 unsigned int x, y, i;
437 unsigned int fbdev_w, fbdev_h, fbdev_bpp, fbdev_pitches;
438
439 struct sunxi_drm_fb *sunxi_fb;
440 struct sunxi_drm_fbdev *fbdev;
441
442 /*copy the bootlogo to every fbdev fb that has been created*/
443 for (i = 0; i < FBDEV_MAX_NUM; i++) {
444 fbdev = sunxi_drm_fbdev_get_fbdev(i);
445 sunxi_fb = fbdev->sunxi_fb;
446 if (!sunxi_fb) {
447 DRM_ERROR("fbdev do NOT have a coresponsible fb\n");
448 return -1;
449 }
450
451 if (!sunxi_fb->obj[0]) {
452 DRM_ERROR("fbdev's fb do NOT have a coresponsible gem\n");
453 return -1;
454 }
455
456 fbdev_addr = sunxi_fb->obj[0]->vaddr + sunxi_fb->fb.offsets[0];
457 fbdev_w = sunxi_fb->fb.width;
458 fbdev_h = sunxi_fb->fb.height / 2;
459 fbdev_bpp = sunxi_fb->fb.format->cpp[0] * 8;
460 fbdev_pitches = sunxi_fb->fb.pitches[0];
461
462 x = bootlogo->width;
463 y = (bootlogo->height & 0x80000000) ?
464 (-bootlogo->height) : (bootlogo->height);
465
466 DRM_INFO("fbdev_addr:%px offset:%d "
467 "fbdev_w:%d fbdev_h:%d fbdev_bpp:%d pitch:%d x:%d y:%d\n",
468 sunxi_fb->obj[0]->vaddr, sunxi_fb->fb.offsets[0],
469 fbdev_w, fbdev_h, fbdev_bpp, fbdev_pitches, x, y);
470
471 /*if bootlogo is bigger than the fb of fbdev,
472 it is NOT allowed to copy bootlogo to fbdev*/
473 if ((x > fbdev_w) || (y > fbdev_h)) {
474 DRM_ERROR("The weight and height of bmp "
475 "is NOT suit for fbdev, bmp:%d-%d fbdev:%d-%d\n",
476 x, y, fbdev_w, fbdev_h);
477 return -1;
478 }
479
480 if (fbdev_bpp != bootlogo->bpp) {
481 DRM_ERROR("bmp's bpp is NOT suit for fbdev: %d %d\n",
482 fbdev_bpp, bootlogo->bpp);
483 return -1;
484 }
485
486 /*sunxi_drm_copy_bootlogo(fbdev_addr, fbdev_w,
487 fbdev_h, bootlogo);*/
488 Fb_copy_boot_fb(fbdev_addr, bootlogo,
489 fbdev_w, fbdev_h, fbdev_bpp, fbdev_pitches);
490 }
491 return 0;
492 }
493
494