1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2012 Robert N. M. Watson
5 * All rights reserved.
6 *
7 * This software was developed by SRI International and the University of
8 * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
9 * ("CTSRD"), as part of the DARPA CRASH research programme.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #include <sys/cdefs.h>
34 #include <sys/param.h>
35 #include <sys/bus.h>
36 #include <sys/conf.h>
37 #include <sys/consio.h> /* struct vt_mode */
38 #include <sys/endian.h>
39 #include <sys/fbio.h> /* video_adapter_t */
40 #include <sys/lock.h>
41 #include <sys/mutex.h>
42 #include <sys/rman.h>
43 #include <sys/systm.h>
44 #include <sys/uio.h>
45
46 #include <machine/bus.h>
47 #include <machine/resource.h>
48 #include <machine/vm.h>
49
50 #include <dev/terasic/mtl/terasic_mtl.h>
51
52 static d_mmap_t terasic_mtl_reg_mmap;
53 static d_read_t terasic_mtl_reg_read;
54 static d_write_t terasic_mtl_reg_write;
55
56 static struct cdevsw terasic_mtl_reg_cdevsw = {
57 .d_version = D_VERSION,
58 .d_mmap = terasic_mtl_reg_mmap,
59 .d_read = terasic_mtl_reg_read,
60 .d_write = terasic_mtl_reg_write,
61 .d_name = "terasic_mtl_reg",
62 };
63
64 /*
65 * All I/O to/from the MTL register device must be 32-bit, and aligned to
66 * 32-bit.
67 */
68 static int
terasic_mtl_reg_read(struct cdev * dev,struct uio * uio,int flag)69 terasic_mtl_reg_read(struct cdev *dev, struct uio *uio, int flag)
70 {
71 struct terasic_mtl_softc *sc;
72 u_long offset, size;
73 uint32_t v;
74 int error;
75
76 if (uio->uio_offset < 0 || uio->uio_offset % 4 != 0 ||
77 uio->uio_resid % 4 != 0)
78 return (ENODEV);
79 sc = dev->si_drv1;
80 size = rman_get_size(sc->mtl_reg_res);
81 error = 0;
82 if ((uio->uio_offset + uio->uio_resid < 0) ||
83 (uio->uio_offset + uio->uio_resid > size))
84 return (ENODEV);
85 while (uio->uio_resid > 0) {
86 offset = uio->uio_offset;
87 if (offset + sizeof(v) > size)
88 return (ENODEV);
89 v = bus_read_4(sc->mtl_reg_res, offset);
90 error = uiomove(&v, sizeof(v), uio);
91 if (error)
92 return (error);
93 }
94 return (error);
95 }
96
97 static int
terasic_mtl_reg_write(struct cdev * dev,struct uio * uio,int flag)98 terasic_mtl_reg_write(struct cdev *dev, struct uio *uio, int flag)
99 {
100 struct terasic_mtl_softc *sc;
101 u_long offset, size;
102 uint32_t v;
103 int error;
104
105 if (uio->uio_offset < 0 || uio->uio_offset % 4 != 0 ||
106 uio->uio_resid % 4 != 0)
107 return (ENODEV);
108 sc = dev->si_drv1;
109 size = rman_get_size(sc->mtl_reg_res);
110 error = 0;
111 while (uio->uio_resid > 0) {
112 offset = uio->uio_offset;
113 if (offset + sizeof(v) > size)
114 return (ENODEV);
115 error = uiomove(&v, sizeof(v), uio);
116 if (error)
117 return (error);
118 bus_write_4(sc->mtl_reg_res, offset, v);
119 }
120 return (error);
121 }
122
123 static int
terasic_mtl_reg_mmap(struct cdev * dev,vm_ooffset_t offset,vm_paddr_t * paddr,int nprot,vm_memattr_t * memattr)124 terasic_mtl_reg_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr,
125 int nprot, vm_memattr_t *memattr)
126 {
127 struct terasic_mtl_softc *sc;
128 int error;
129
130 sc = dev->si_drv1;
131 error = 0;
132 if (trunc_page(offset) == offset &&
133 offset + PAGE_SIZE > offset &&
134 rman_get_size(sc->mtl_reg_res) >= offset + PAGE_SIZE) {
135 *paddr = rman_get_start(sc->mtl_reg_res) + offset;
136 *memattr = VM_MEMATTR_UNCACHEABLE;
137 } else
138 error = ENODEV;
139 return (error);
140 }
141
142 void
terasic_mtl_reg_blend_get(struct terasic_mtl_softc * sc,uint32_t * blendp)143 terasic_mtl_reg_blend_get(struct terasic_mtl_softc *sc, uint32_t *blendp)
144 {
145
146 *blendp = le32toh(bus_read_4(sc->mtl_reg_res, TERASIC_MTL_OFF_BLEND));
147 }
148
149 void
terasic_mtl_reg_blend_set(struct terasic_mtl_softc * sc,uint32_t blend)150 terasic_mtl_reg_blend_set(struct terasic_mtl_softc *sc, uint32_t blend)
151 {
152
153 bus_write_4(sc->mtl_reg_res, TERASIC_MTL_OFF_BLEND, htole32(blend));
154 }
155
156 void
terasic_mtl_blend_default_set(struct terasic_mtl_softc * sc,uint8_t colour)157 terasic_mtl_blend_default_set(struct terasic_mtl_softc *sc, uint8_t colour)
158 {
159 uint32_t v;
160
161 TERASIC_MTL_LOCK(sc);
162 terasic_mtl_reg_blend_get(sc, &v);
163 v &= ~TERASIC_MTL_BLEND_DEFAULT_MASK;
164 v |= colour << TERASIC_MTL_BLEND_DEFAULT_SHIFT;
165 terasic_mtl_reg_blend_set(sc, v);
166 TERASIC_MTL_UNLOCK(sc);
167 }
168
169 void
terasic_mtl_blend_pixel_set(struct terasic_mtl_softc * sc,uint8_t alpha)170 terasic_mtl_blend_pixel_set(struct terasic_mtl_softc *sc, uint8_t alpha)
171 {
172 uint32_t v;
173
174 TERASIC_MTL_LOCK(sc);
175 terasic_mtl_reg_blend_get(sc, &v);
176 v &= ~TERASIC_MTL_BLEND_PIXEL_MASK;
177 v |= alpha << TERASIC_MTL_BLEND_PIXEL_SHIFT;
178 terasic_mtl_reg_blend_set(sc, v);
179 TERASIC_MTL_UNLOCK(sc);
180 }
181
182 void
terasic_mtl_blend_textfg_set(struct terasic_mtl_softc * sc,uint8_t alpha)183 terasic_mtl_blend_textfg_set(struct terasic_mtl_softc *sc, uint8_t alpha)
184 {
185 uint32_t v;
186
187 TERASIC_MTL_LOCK(sc);
188 terasic_mtl_reg_blend_get(sc, &v);
189 v &= ~TERASIC_MTL_BLEND_TEXTFG_MASK;
190 v |= alpha << TERASIC_MTL_BLEND_TEXTFG_SHIFT;
191 terasic_mtl_reg_blend_set(sc, v);
192 TERASIC_MTL_UNLOCK(sc);
193 }
194
195 void
terasic_mtl_blend_textbg_set(struct terasic_mtl_softc * sc,uint8_t alpha)196 terasic_mtl_blend_textbg_set(struct terasic_mtl_softc *sc, uint8_t alpha)
197 {
198 uint32_t v;
199
200 TERASIC_MTL_LOCK(sc);
201 terasic_mtl_reg_blend_get(sc, &v);
202 v &= ~TERASIC_MTL_BLEND_TEXTBG_MASK;
203 v |= alpha << TERASIC_MTL_BLEND_TEXTBG_SHIFT;
204 terasic_mtl_reg_blend_set(sc, v);
205 TERASIC_MTL_UNLOCK(sc);
206 }
207
208 void
terasic_mtl_reg_pixel_endian_set(struct terasic_mtl_softc * sc,int endian_swap)209 terasic_mtl_reg_pixel_endian_set(struct terasic_mtl_softc *sc, int endian_swap)
210 {
211 uint32_t v;
212
213 TERASIC_MTL_LOCK(sc);
214 terasic_mtl_reg_blend_get(sc, &v);
215 if (endian_swap)
216 v |= TERASIC_MTL_BLEND_PIXEL_ENDIAN_SWAP;
217 else
218 v &= ~TERASIC_MTL_BLEND_PIXEL_ENDIAN_SWAP;
219 terasic_mtl_reg_blend_set(sc, v);
220 TERASIC_MTL_UNLOCK(sc);
221 }
222
223 void
terasic_mtl_reg_textcursor_get(struct terasic_mtl_softc * sc,uint8_t * colp,uint8_t * rowp)224 terasic_mtl_reg_textcursor_get(struct terasic_mtl_softc *sc, uint8_t *colp,
225 uint8_t *rowp)
226 {
227 uint32_t v;
228
229 v = bus_read_4(sc->mtl_reg_res, TERASIC_MTL_OFF_TEXTCURSOR);
230 v = le32toh(v);
231 *colp = (v & TERASIC_MTL_TEXTCURSOR_COL_MASK) >>
232 TERASIC_MTL_TEXTCURSOR_COL_SHIFT;
233 *rowp = (v & TERASIC_MTL_TEXTCURSOR_ROW_MASK);
234 }
235
236 void
terasic_mtl_reg_textcursor_set(struct terasic_mtl_softc * sc,uint8_t col,uint8_t row)237 terasic_mtl_reg_textcursor_set(struct terasic_mtl_softc *sc, uint8_t col,
238 uint8_t row)
239 {
240 uint32_t v;
241
242 v = (col << TERASIC_MTL_TEXTCURSOR_COL_SHIFT) | row;
243 v = htole32(v);
244 bus_write_4(sc->mtl_reg_res, TERASIC_MTL_OFF_TEXTCURSOR, v);
245 }
246
247 void
terasic_mtl_reg_blank(struct terasic_mtl_softc * sc)248 terasic_mtl_reg_blank(struct terasic_mtl_softc *sc)
249 {
250
251 device_printf(sc->mtl_dev, "%s: not yet\n", __func__);
252 }
253
254 void
terasic_mtl_reg_textframebufaddr_get(struct terasic_mtl_softc * sc,uint32_t * addrp)255 terasic_mtl_reg_textframebufaddr_get(struct terasic_mtl_softc *sc,
256 uint32_t *addrp)
257 {
258 uint32_t addr;
259
260 addr = bus_read_4(sc->mtl_reg_res, TERASIC_MTL_OFF_TEXTFRAMEBUFADDR);
261 *addrp = le32toh(addr);
262 }
263
264 void
terasic_mtl_reg_textframebufaddr_set(struct terasic_mtl_softc * sc,uint32_t addr)265 terasic_mtl_reg_textframebufaddr_set(struct terasic_mtl_softc *sc,
266 uint32_t addr)
267 {
268
269 addr = htole32(addr);
270 bus_write_4(sc->mtl_reg_res, TERASIC_MTL_OFF_TEXTFRAMEBUFADDR, addr);
271 }
272
273 int
terasic_mtl_reg_attach(struct terasic_mtl_softc * sc)274 terasic_mtl_reg_attach(struct terasic_mtl_softc *sc)
275 {
276
277 sc->mtl_reg_cdev = make_dev(&terasic_mtl_reg_cdevsw, sc->mtl_unit,
278 UID_ROOT, GID_WHEEL, 0400, "mtl_reg%d", sc->mtl_unit);
279 if (sc->mtl_reg_cdev == NULL) {
280 device_printf(sc->mtl_dev, "%s: make_dev failed\n", __func__);
281 return (ENXIO);
282 }
283 /* XXXRW: Slight race between make_dev(9) and here. */
284 sc->mtl_reg_cdev->si_drv1 = sc;
285 return (0);
286 }
287
288 void
terasic_mtl_reg_detach(struct terasic_mtl_softc * sc)289 terasic_mtl_reg_detach(struct terasic_mtl_softc *sc)
290 {
291
292 if (sc->mtl_reg_cdev != NULL)
293 destroy_dev(sc->mtl_reg_cdev);
294 }
295