1*ae2cfe2aSjmcneill /* $NetBSD: fb.c,v 1.5 2017/08/27 02:19:08 jmcneill Exp $ */
2b8ce6e82Stakemura
3b8ce6e82Stakemura /*-
4b8ce6e82Stakemura * Copyright (c) 2002 TAKEMRUA Shin
5b8ce6e82Stakemura * All rights reserved.
6b8ce6e82Stakemura *
7b8ce6e82Stakemura * Redistribution and use in source and binary forms, with or without
8b8ce6e82Stakemura * modification, are permitted provided that the following conditions
9b8ce6e82Stakemura * are met:
10b8ce6e82Stakemura * 1. Redistributions of source code must retain the above copyright
11b8ce6e82Stakemura * notice, this list of conditions and the following disclaimer.
12b8ce6e82Stakemura * 2. Redistributions in binary form must reproduce the above copyright
13b8ce6e82Stakemura * notice, this list of conditions and the following disclaimer in the
14b8ce6e82Stakemura * documentation and/or other materials provided with the distribution.
155d1469bdSmartin * 3. Neither the name of The NetBSD Foundation nor the names of its
165d1469bdSmartin * contributors may be used to endorse or promote products derived
175d1469bdSmartin * from this software without specific prior written permission.
18b8ce6e82Stakemura *
19b8ce6e82Stakemura * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20b8ce6e82Stakemura * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21b8ce6e82Stakemura * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22b8ce6e82Stakemura * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23b8ce6e82Stakemura * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24b8ce6e82Stakemura * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25b8ce6e82Stakemura * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26b8ce6e82Stakemura * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27b8ce6e82Stakemura * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28b8ce6e82Stakemura * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29b8ce6e82Stakemura * POSSIBILITY OF SUCH DAMAGE.
30b8ce6e82Stakemura */
31b8ce6e82Stakemura
3299b1f08dSuwe #include <string.h>
33b8ce6e82Stakemura #include <stdio.h>
34b8ce6e82Stakemura #include <unistd.h>
35b8ce6e82Stakemura #include <stdlib.h>
36b8ce6e82Stakemura #include <sys/ioctl.h>
37b8ce6e82Stakemura #include <sys/fcntl.h>
38b8ce6e82Stakemura #include <sys/mman.h>
39b8ce6e82Stakemura
40b8ce6e82Stakemura #include "tpctl.h"
41b8ce6e82Stakemura
42b8ce6e82Stakemura #ifndef lint
43b8ce6e82Stakemura #include <sys/cdefs.h>
44*ae2cfe2aSjmcneill __RCSID("$NetBSD: fb.c,v 1.5 2017/08/27 02:19:08 jmcneill Exp $");
45b8ce6e82Stakemura #endif /* not lint */
46b8ce6e82Stakemura
47b8ce6e82Stakemura #define INVALID_CACHE -1
48b8ce6e82Stakemura #define ALIGN(a, n) ((typeof(a))(((int)(a) + (n) - 1) / (n) * (n)))
49b8ce6e82Stakemura #define ABS(a) ((a) < 0 ? -(a) : (a))
50b8ce6e82Stakemura #define SWAP(a, b) do { \
51b8ce6e82Stakemura typeof(a) tmp; \
52b8ce6e82Stakemura tmp = (a); (a) = (b); (b) = tmp; \
53b8ce6e82Stakemura } while(0)
54b8ce6e82Stakemura #define bitsizeof(t) (sizeof(t) * 8)
55b8ce6e82Stakemura
56b8ce6e82Stakemura int
fb_dispmode(struct fb * fb,int dispmode)57b8ce6e82Stakemura fb_dispmode(struct fb *fb, int dispmode)
58b8ce6e82Stakemura {
59b8ce6e82Stakemura
60b8ce6e82Stakemura if (fb->dispmode != dispmode) {
61b8ce6e82Stakemura if (ioctl(fb->fd, WSDISPLAYIO_SMODE, &dispmode) < 0)
62b8ce6e82Stakemura return (-1);
63b8ce6e82Stakemura fb->dispmode = dispmode;
64b8ce6e82Stakemura }
65b8ce6e82Stakemura
66b8ce6e82Stakemura return (0);
67b8ce6e82Stakemura }
68b8ce6e82Stakemura
69b8ce6e82Stakemura int
fb_init(struct fb * fb,int fd)70b8ce6e82Stakemura fb_init(struct fb *fb, int fd)
71b8ce6e82Stakemura {
72*ae2cfe2aSjmcneill struct wsdisplay_fbinfo fbinfo;
73*ae2cfe2aSjmcneill u_int linebytes;
74b8ce6e82Stakemura int y;
75b8ce6e82Stakemura size_t size;
76b8ce6e82Stakemura
77b8ce6e82Stakemura fb->fd = fd;
78b8ce6e82Stakemura fb->linecache_y = INVALID_CACHE;
79b8ce6e82Stakemura fb->conf.hf_conf_index = HPCFB_CURRENT_CONFIG;
80b8ce6e82Stakemura if (ioctl(fb->fd, WSDISPLAYIO_GMODE, &fb->dispmode) < 0)
81b8ce6e82Stakemura return (-1);
82*ae2cfe2aSjmcneill if (ioctl(fb->fd, HPCFBIO_GCONF, &fb->conf) < 0) {
83*ae2cfe2aSjmcneill if (ioctl(fb->fd, WSDISPLAYIO_GINFO, &fbinfo) < 0 ||
84*ae2cfe2aSjmcneill ioctl(fb->fd, WSDISPLAYIO_LINEBYTES, &linebytes) < 0)
85b8ce6e82Stakemura return (-1);
86*ae2cfe2aSjmcneill memset(&fb->conf, 0, sizeof(fb->conf));
87*ae2cfe2aSjmcneill fb->conf.hf_width = fbinfo.width;
88*ae2cfe2aSjmcneill fb->conf.hf_height = fbinfo.height;
89*ae2cfe2aSjmcneill fb->conf.hf_bytes_per_line = linebytes;
90*ae2cfe2aSjmcneill fb->conf.hf_nplanes = 1;
91*ae2cfe2aSjmcneill fb->conf.hf_bytes_per_plane = fbinfo.height * linebytes;
92*ae2cfe2aSjmcneill fb->conf.hf_pack_width = fbinfo.depth;
93*ae2cfe2aSjmcneill fb->conf.hf_pixels_per_pack = 1;
94*ae2cfe2aSjmcneill fb->conf.hf_pixel_width = 1;
95*ae2cfe2aSjmcneill fb->conf.hf_access_flags = HPCFB_ACCESS_STATIC |
96*ae2cfe2aSjmcneill HPCFB_ACCESS_BYTE | HPCFB_ACCESS_WORD | HPCFB_ACCESS_DWORD;
97*ae2cfe2aSjmcneill }
98b8ce6e82Stakemura
99b8ce6e82Stakemura if (fb_dispmode(fb, WSDISPLAYIO_MODE_MAPPED) < 0)
100b8ce6e82Stakemura return (-1);
101b8ce6e82Stakemura
102b8ce6e82Stakemura size = (size_t)fb->conf.hf_bytes_per_line * fb->conf.hf_height;
103b8ce6e82Stakemura size += fb->conf.hf_offset;
104b8ce6e82Stakemura size = ALIGN(size, getpagesize());
105b8ce6e82Stakemura fb->baseaddr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd,0);
106b8ce6e82Stakemura if (fb->baseaddr == MAP_FAILED)
107b8ce6e82Stakemura return (-1);
108b8ce6e82Stakemura fb->baseaddr += fb->conf.hf_offset;
109b8ce6e82Stakemura
110b8ce6e82Stakemura size = ALIGN(fb->conf.hf_bytes_per_line, 16);
111b8ce6e82Stakemura fb->linecache = (fb_pixel_t*)malloc(size);
112b8ce6e82Stakemura if (fb->linecache == NULL)
113b8ce6e82Stakemura return (-1);
114b8ce6e82Stakemura fb->workbuf = (fb_pixel_t*)malloc(size);
115b8ce6e82Stakemura if (fb->workbuf == NULL)
116b8ce6e82Stakemura return (-1);
117b8ce6e82Stakemura
118b8ce6e82Stakemura if (fb->conf.hf_access_flags & HPCFB_ACCESS_REVERSE) {
119b8ce6e82Stakemura fb->white = 0;
120b8ce6e82Stakemura fb->black = ~0;
121b8ce6e82Stakemura } else {
122b8ce6e82Stakemura fb->white = ~0;
123b8ce6e82Stakemura fb->black = 0;
124b8ce6e82Stakemura }
125b8ce6e82Stakemura
126b8ce6e82Stakemura /*
127b8ce6e82Stakemura * clear screen
128b8ce6e82Stakemura */
129b8ce6e82Stakemura for (y = 0; y < fb->conf.hf_height; y++) {
130b8ce6e82Stakemura fb_getline(fb, y);
131b8ce6e82Stakemura memset(fb->linecache, fb->black,
132b8ce6e82Stakemura ALIGN(fb->conf.hf_bytes_per_line, 16));
133b8ce6e82Stakemura fb_putline(fb, y);
134b8ce6e82Stakemura }
135b8ce6e82Stakemura
136b8ce6e82Stakemura return (0);
137b8ce6e82Stakemura }
138b8ce6e82Stakemura
139b8ce6e82Stakemura static void
__fb_swap_workbuf(struct fb * fb)140b8ce6e82Stakemura __fb_swap_workbuf(struct fb *fb)
141b8ce6e82Stakemura {
142b8ce6e82Stakemura int i, n;
143b8ce6e82Stakemura
144b8ce6e82Stakemura n = ALIGN(fb->conf.hf_bytes_per_line, 16) / sizeof(fb_pixel_t);
145b8ce6e82Stakemura if (fb->conf.hf_order_flags & HPCFB_REVORDER_BYTE) {
146b8ce6e82Stakemura for (i = 0; i < n; i++)
147b8ce6e82Stakemura fb->workbuf[i] =
148b8ce6e82Stakemura ((fb->workbuf[i] << 8) & 0xff00ff00) |
149b8ce6e82Stakemura ((fb->workbuf[i] >> 8) & 0x00ff00ff);
150b8ce6e82Stakemura }
151b8ce6e82Stakemura if (fb->conf.hf_order_flags & HPCFB_REVORDER_WORD) {
152b8ce6e82Stakemura for (i = 0; i < n; i++)
153b8ce6e82Stakemura fb->workbuf[i] =
154b8ce6e82Stakemura ((fb->workbuf[i] << 16) & 0xffff0000) |
155b8ce6e82Stakemura ((fb->workbuf[i] >> 16) & 0x0000ffff);
156b8ce6e82Stakemura }
157b8ce6e82Stakemura if (fb->conf.hf_order_flags & HPCFB_REVORDER_DWORD) {
158b8ce6e82Stakemura for (i = 0; i < n; i += 2) {
159b8ce6e82Stakemura fb_pixel_t tmp;
160b8ce6e82Stakemura tmp = fb->workbuf[i];
161b8ce6e82Stakemura fb->workbuf[i] = fb->workbuf[i + 1];
162b8ce6e82Stakemura fb->workbuf[i + 1] = tmp;
163b8ce6e82Stakemura }
164b8ce6e82Stakemura }
165b8ce6e82Stakemura if (fb->conf.hf_order_flags & HPCFB_REVORDER_QWORD) {
166b8ce6e82Stakemura for (i = 0; i < n; i += 4) {
167b8ce6e82Stakemura fb_pixel_t tmp;
168b8ce6e82Stakemura tmp = fb->workbuf[i + 0];
169b8ce6e82Stakemura fb->workbuf[i + 0] = fb->workbuf[i + 2];
170b8ce6e82Stakemura fb->workbuf[i + 2] = tmp;
171b8ce6e82Stakemura tmp = fb->workbuf[i + 1];
172b8ce6e82Stakemura fb->workbuf[i + 1] = fb->workbuf[i + 3];
173b8ce6e82Stakemura fb->workbuf[i + 3] = tmp;
174b8ce6e82Stakemura }
175b8ce6e82Stakemura }
176b8ce6e82Stakemura }
177b8ce6e82Stakemura
178b8ce6e82Stakemura static void
__fb_put_pixel(struct fb * fb,fb_pixel_t pixel,int width,int x)179b8ce6e82Stakemura __fb_put_pixel(struct fb *fb, fb_pixel_t pixel, int width, int x)
180b8ce6e82Stakemura {
181b8ce6e82Stakemura fb_pixel_t mask = (1 << width) - 1;
182b8ce6e82Stakemura
183b8ce6e82Stakemura x -= (bitsizeof(fb_pixel_t) - width);
184b8ce6e82Stakemura if (x < 0) {
185b8ce6e82Stakemura pixel <<= -x;
186b8ce6e82Stakemura mask <<= -x;
187b8ce6e82Stakemura fb->linecache[0] = (fb->linecache[0]&~mask) | (pixel&~mask);
188b8ce6e82Stakemura } else {
189b8ce6e82Stakemura fb_pixel_t *dst = &fb->linecache[x / bitsizeof(fb_pixel_t)];
190b8ce6e82Stakemura x %= bitsizeof(fb_pixel_t);
191b8ce6e82Stakemura *dst = (*dst & ~(mask>>x)) | ((pixel>>x) & (mask>>x));
192b8ce6e82Stakemura dst++;
193b8ce6e82Stakemura if (x == 0)
194b8ce6e82Stakemura return;
195b8ce6e82Stakemura x = bitsizeof(fb_pixel_t) - x;
196b8ce6e82Stakemura *dst = (*dst & ~(mask<<x)) | ((pixel<<x) & (mask<<x));
197b8ce6e82Stakemura }
198b8ce6e82Stakemura }
199b8ce6e82Stakemura
200b8ce6e82Stakemura void
fb_getline(struct fb * fb,int y)201b8ce6e82Stakemura fb_getline(struct fb *fb, int y)
202b8ce6e82Stakemura {
203b8ce6e82Stakemura int i, n;
204b8ce6e82Stakemura unsigned char *src;
205b8ce6e82Stakemura fb_pixel_t *dst;
206b8ce6e82Stakemura
207b8ce6e82Stakemura src = fb->baseaddr + fb->conf.hf_bytes_per_line * y;
208b8ce6e82Stakemura dst = fb->workbuf;
209b8ce6e82Stakemura n = ALIGN(fb->conf.hf_bytes_per_line, 16) / sizeof(fb_pixel_t);
210b8ce6e82Stakemura for (i = 0; i < n; i++) {
211b8ce6e82Stakemura *dst++ = ((fb_pixel_t)src[0] << 24) |
212b8ce6e82Stakemura ((fb_pixel_t)src[1] << 16) |
213b8ce6e82Stakemura ((fb_pixel_t)src[2] << 8) |
214b8ce6e82Stakemura ((fb_pixel_t)src[3] << 0);
215b8ce6e82Stakemura src += 4;
216b8ce6e82Stakemura }
217b8ce6e82Stakemura
218b8ce6e82Stakemura __fb_swap_workbuf(fb);
219b8ce6e82Stakemura memcpy(fb->linecache, fb->workbuf, n * sizeof(fb_pixel_t));
220b8ce6e82Stakemura }
221b8ce6e82Stakemura
222b8ce6e82Stakemura void
fb_putline(struct fb * fb,int y)223b8ce6e82Stakemura fb_putline(struct fb *fb, int y)
224b8ce6e82Stakemura {
225b8ce6e82Stakemura int i, n;
226b8ce6e82Stakemura unsigned char *dst;
227b8ce6e82Stakemura fb_pixel_t *src;
228b8ce6e82Stakemura
229b8ce6e82Stakemura src = fb->workbuf;
230b8ce6e82Stakemura dst = fb->baseaddr + fb->conf.hf_bytes_per_line * y;
231b8ce6e82Stakemura n = ALIGN(fb->conf.hf_bytes_per_line, 16) / sizeof(fb_pixel_t);
232b8ce6e82Stakemura memcpy(fb->workbuf, fb->linecache, n * sizeof(fb_pixel_t));
233b8ce6e82Stakemura __fb_swap_workbuf(fb);
234b8ce6e82Stakemura for (i = 0; i < n; i++) {
235b8ce6e82Stakemura *dst++ = (*src >> 24) & 0xff;
236b8ce6e82Stakemura *dst++ = (*src >> 16) & 0xff;
237b8ce6e82Stakemura *dst++ = (*src >> 8) & 0xff;
238b8ce6e82Stakemura *dst++ = (*src >> 0) & 0xff;
239b8ce6e82Stakemura src++;
240b8ce6e82Stakemura }
241b8ce6e82Stakemura }
242b8ce6e82Stakemura
243b8ce6e82Stakemura void
fb_fetchline(struct fb * fb,int y)244b8ce6e82Stakemura fb_fetchline(struct fb *fb, int y)
245b8ce6e82Stakemura {
246b8ce6e82Stakemura if (fb->linecache_y == y)
247b8ce6e82Stakemura return;
248b8ce6e82Stakemura fb_getline(fb, y);
249b8ce6e82Stakemura fb->linecache_y = y;
250b8ce6e82Stakemura }
251b8ce6e82Stakemura
252b8ce6e82Stakemura void
fb_flush(struct fb * fb)253b8ce6e82Stakemura fb_flush(struct fb *fb)
254b8ce6e82Stakemura {
255b8ce6e82Stakemura if (fb->linecache_y != INVALID_CACHE)
256b8ce6e82Stakemura fb_putline(fb, fb->linecache_y);
257b8ce6e82Stakemura }
258b8ce6e82Stakemura
259b8ce6e82Stakemura void
fb_drawpixel(struct fb * fb,int x,int y,fb_pixel_t pixel)260b8ce6e82Stakemura fb_drawpixel(struct fb *fb, int x, int y, fb_pixel_t pixel)
261b8ce6e82Stakemura {
262b8ce6e82Stakemura int pack;
263b8ce6e82Stakemura
264b8ce6e82Stakemura if (fb->conf.hf_access_flags & HPCFB_ACCESS_Y_TO_X)
265b8ce6e82Stakemura SWAP(x, y);
266b8ce6e82Stakemura if (fb->conf.hf_access_flags & HPCFB_ACCESS_R_TO_L)
267b8ce6e82Stakemura x = fb->conf.hf_width - x - 1;
268b8ce6e82Stakemura if (fb->conf.hf_access_flags & HPCFB_ACCESS_B_TO_T)
269b8ce6e82Stakemura y = fb->conf.hf_height - y - 1;
270b8ce6e82Stakemura
271b8ce6e82Stakemura if (x < 0 || y < 0 || fb->conf.hf_width <= x || fb->conf.hf_height < y)
272b8ce6e82Stakemura return;
273b8ce6e82Stakemura
274b8ce6e82Stakemura pack = x / fb->conf.hf_pixels_per_pack;
275b8ce6e82Stakemura x %= fb->conf.hf_pixels_per_pack;
276b8ce6e82Stakemura if (fb->conf.hf_access_flags & HPCFB_ACCESS_LSB_TO_MSB)
277b8ce6e82Stakemura x = fb->conf.hf_pixels_per_pack - x - 1;
278b8ce6e82Stakemura x *= fb->conf.hf_pixel_width;
279b8ce6e82Stakemura if (fb->conf.hf_access_flags & HPCFB_ACCESS_PACK_BLANK)
280b8ce6e82Stakemura x += (fb->conf.hf_pack_width -
281b8ce6e82Stakemura fb->conf.hf_pixel_width * fb->conf.hf_pixels_per_pack);
282b8ce6e82Stakemura x += pack * fb->conf.hf_pack_width;
283b8ce6e82Stakemura
284b8ce6e82Stakemura if (fb->linecache_y != y) {
285b8ce6e82Stakemura fb_flush(fb);
286b8ce6e82Stakemura fb_fetchline(fb, y);
287b8ce6e82Stakemura }
288b8ce6e82Stakemura
289b8ce6e82Stakemura __fb_put_pixel(fb, pixel, fb->conf.hf_pixel_width, x);
290b8ce6e82Stakemura }
291b8ce6e82Stakemura
292b8ce6e82Stakemura void
fb_drawline(struct fb * fb,int x0,int y0,int x1,int y1,fb_pixel_t pixel)293b8ce6e82Stakemura fb_drawline(struct fb *fb, int x0, int y0, int x1, int y1, fb_pixel_t pixel)
294b8ce6e82Stakemura {
295b8ce6e82Stakemura int i, dx, dy, d, incdec;
296b8ce6e82Stakemura
297b8ce6e82Stakemura dx = ABS(x1 - x0);
298b8ce6e82Stakemura dy = ABS(y1 - y0);
299b8ce6e82Stakemura if (dx < dy) {
300b8ce6e82Stakemura if (y1 < y0) {
301b8ce6e82Stakemura SWAP(x0, x1);
302b8ce6e82Stakemura SWAP(y0, y1);
303b8ce6e82Stakemura }
304b8ce6e82Stakemura if (x0 < x1)
305b8ce6e82Stakemura incdec = 1;
306b8ce6e82Stakemura else
307b8ce6e82Stakemura incdec = -1;
308b8ce6e82Stakemura d = -dy;
309b8ce6e82Stakemura dx *= 2;
310b8ce6e82Stakemura dy *= 2;
311b8ce6e82Stakemura for (i = y0; i <= y1; i++) {
312b8ce6e82Stakemura fb_drawpixel(fb, x0, i, pixel);
313b8ce6e82Stakemura d += dx;
314b8ce6e82Stakemura if (0 <= d) {
315b8ce6e82Stakemura d -= dy;
316b8ce6e82Stakemura x0 += incdec;
317b8ce6e82Stakemura }
318b8ce6e82Stakemura }
319b8ce6e82Stakemura } else {
320b8ce6e82Stakemura if (x1 < x0) {
321b8ce6e82Stakemura SWAP(x0, x1);
322b8ce6e82Stakemura SWAP(y0, y1);
323b8ce6e82Stakemura }
324b8ce6e82Stakemura if (y0 < y1)
325b8ce6e82Stakemura incdec = 1;
326b8ce6e82Stakemura else
327b8ce6e82Stakemura incdec = -1;
328b8ce6e82Stakemura d = -dx;
329b8ce6e82Stakemura dx *= 2;
330b8ce6e82Stakemura dy *= 2;
331b8ce6e82Stakemura for (i = x0; i <= x1; i++) {
332b8ce6e82Stakemura fb_drawpixel(fb, i, y0, pixel);
333b8ce6e82Stakemura d += dy;
334b8ce6e82Stakemura if (0 <= d) {
335b8ce6e82Stakemura d -= dx;
336b8ce6e82Stakemura y0 += incdec;
337b8ce6e82Stakemura }
338b8ce6e82Stakemura }
339b8ce6e82Stakemura }
340b8ce6e82Stakemura }
341