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