• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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