1/* $NetBSD: nouveau_engine_disp_vga.c,v 1.1.1.1 2014/08/06 12:36:24 riastradh Exp $ */
2
3/*
4 * Copyright 2012 Red Hat Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 *
24 * Authors: Ben Skeggs
25 */
26
27#include <sys/cdefs.h>
28__KERNEL_RCSID(0, "$NetBSD: nouveau_engine_disp_vga.c,v 1.1.1.1 2014/08/06 12:36:24 riastradh Exp $");
29
30#include <core/subdev.h>
31#include <core/device.h>
32#include <subdev/vga.h>
33
34u8
35nv_rdport(void *obj, int head, u16 port)
36{
37 struct nouveau_device *device = nv_device(obj);
38
39 if (device->card_type >= NV_50)
40 return nv_rd08(obj, 0x601000 + port);
41
42 if (port == 0x03c0 || port == 0x03c1 || /* AR */
43 port == 0x03c2 || port == 0x03da || /* INP0 */
44 port == 0x03d4 || port == 0x03d5) /* CR */
45 return nv_rd08(obj, 0x601000 + (head * 0x2000) + port);
46
47 if (port == 0x03c2 || port == 0x03cc || /* MISC */
48 port == 0x03c4 || port == 0x03c5 || /* SR */
49 port == 0x03ce || port == 0x03cf) { /* GR */
50 if (device->card_type < NV_40)
51 head = 0; /* CR44 selects head */
52 return nv_rd08(obj, 0x0c0000 + (head * 0x2000) + port);
53 }
54
55 nv_error(obj, "unknown vga port 0x%04x\n", port);
56 return 0x00;
57}
58
59void
60nv_wrport(void *obj, int head, u16 port, u8 data)
61{
62 struct nouveau_device *device = nv_device(obj);
63
64 if (device->card_type >= NV_50)
65 nv_wr08(obj, 0x601000 + port, data);
66 else
67 if (port == 0x03c0 || port == 0x03c1 || /* AR */
68 port == 0x03c2 || port == 0x03da || /* INP0 */
69 port == 0x03d4 || port == 0x03d5) /* CR */
70 nv_wr08(obj, 0x601000 + (head * 0x2000) + port, data);
71 else
72 if (port == 0x03c2 || port == 0x03cc || /* MISC */
73 port == 0x03c4 || port == 0x03c5 || /* SR */
74 port == 0x03ce || port == 0x03cf) { /* GR */
75 if (device->card_type < NV_40)
76 head = 0; /* CR44 selects head */
77 nv_wr08(obj, 0x0c0000 + (head * 0x2000) + port, data);
78 } else
79 nv_error(obj, "unknown vga port 0x%04x\n", port);
80}
81
82u8
83nv_rdvgas(void *obj, int head, u8 index)
84{
85 nv_wrport(obj, head, 0x03c4, index);
86 return nv_rdport(obj, head, 0x03c5);
87}
88
89void
90nv_wrvgas(void *obj, int head, u8 index, u8 value)
91{
92 nv_wrport(obj, head, 0x03c4, index);
93 nv_wrport(obj, head, 0x03c5, value);
94}
95
96u8
97nv_rdvgag(void *obj, int head, u8 index)
98{
99 nv_wrport(obj, head, 0x03ce, index);
100 return nv_rdport(obj, head, 0x03cf);
101}
102
103void
104nv_wrvgag(void *obj, int head, u8 index, u8 value)
105{
106 nv_wrport(obj, head, 0x03ce, index);
107 nv_wrport(obj, head, 0x03cf, value);
108}
109
110u8
111nv_rdvgac(void *obj, int head, u8 index)
112{
113 nv_wrport(obj, head, 0x03d4, index);
114 return nv_rdport(obj, head, 0x03d5);
115}
116
117void
118nv_wrvgac(void *obj, int head, u8 index, u8 value)
119{
120 nv_wrport(obj, head, 0x03d4, index);
121 nv_wrport(obj, head, 0x03d5, value);
122}
123
124u8
125nv_rdvgai(void *obj, int head, u16 port, u8 index)
126{
127 if (port == 0x03c4) return nv_rdvgas(obj, head, index);
128 if (port == 0x03ce) return nv_rdvgag(obj, head, index);
129 if (port == 0x03d4) return nv_rdvgac(obj, head, index);
130 nv_error(obj, "unknown indexed vga port 0x%04x\n", port);
131 return 0x00;
132}
133
134void
135nv_wrvgai(void *obj, int head, u16 port, u8 index, u8 value)
136{
137 if (port == 0x03c4) nv_wrvgas(obj, head, index, value);
138 else if (port == 0x03ce) nv_wrvgag(obj, head, index, value);
139 else if (port == 0x03d4) nv_wrvgac(obj, head, index, value);
140 else nv_error(obj, "unknown indexed vga port 0x%04x\n", port);
141}
142
143bool
144nv_lockvgac(void *obj, bool lock)
145{
146 struct nouveau_device *dev = nv_device(obj);
147
148 bool locked = !nv_rdvgac(obj, 0, 0x1f);
149 u8 data = lock ? 0x99 : 0x57;
150 if (dev->card_type < NV_50)
151 nv_wrvgac(obj, 0, 0x1f, data);
152 else
153 nv_wrvgac(obj, 0, 0x3f, data);
154 if (dev->chipset == 0x11) {
155 if (!(nv_rd32(obj, 0x001084) & 0x10000000))
156 nv_wrvgac(obj, 1, 0x1f, data);
157 }
158 return locked;
159}
160
161/* CR44 takes values 0 (head A), 3 (head B) and 4 (heads tied)
162 * it affects only the 8 bit vga io regs, which we access using mmio at
163 * 0xc{0,2}3c*, 0x60{1,3}3*, and 0x68{1,3}3d*
164 * in general, the set value of cr44 does not matter: reg access works as
165 * expected and values can be set for the appropriate head by using a 0x2000
166 * offset as required
167 * however:
168 * a) pre nv40, the head B range of PRMVIO regs at 0xc23c* was not exposed and
169 * cr44 must be set to 0 or 3 for accessing values on the correct head
170 * through the common 0xc03c* addresses
171 * b) in tied mode (4) head B is programmed to the values set on head A, and
172 * access using the head B addresses can have strange results, ergo we leave
173 * tied mode in init once we know to what cr44 should be restored on exit
174 *
175 * the owner parameter is slightly abused:
176 * 0 and 1 are treated as head values and so the set value is (owner * 3)
177 * other values are treated as literal values to set
178 */
179u8
180nv_rdvgaowner(void *obj)
181{
182 if (nv_device(obj)->card_type < NV_50) {
183 if (nv_device(obj)->chipset == 0x11) {
184 u32 tied = nv_rd32(obj, 0x001084) & 0x10000000;
185 if (tied == 0) {
186 u8 slA = nv_rdvgac(obj, 0, 0x28) & 0x80;
187 u8 tvA = nv_rdvgac(obj, 0, 0x33) & 0x01;
188 u8 slB = nv_rdvgac(obj, 1, 0x28) & 0x80;
189 u8 tvB = nv_rdvgac(obj, 1, 0x33) & 0x01;
190 if (slA && !tvA) return 0x00;
191 if (slB && !tvB) return 0x03;
192 if (slA) return 0x00;
193 if (slB) return 0x03;
194 return 0x00;
195 }
196 return 0x04;
197 }
198
199 return nv_rdvgac(obj, 0, 0x44);
200 }
201
202 nv_error(obj, "rdvgaowner after nv4x\n");
203 return 0x00;
204}
205
206void
207nv_wrvgaowner(void *obj, u8 select)
208{
209 if (nv_device(obj)->card_type < NV_50) {
210 u8 owner = (select == 1) ? 3 : select;
211 if (nv_device(obj)->chipset == 0x11) {
212 /* workaround hw lockup bug */
213 nv_rdvgac(obj, 0, 0x1f);
214 nv_rdvgac(obj, 1, 0x1f);
215 }
216
217 nv_wrvgac(obj, 0, 0x44, owner);
218
219 if (nv_device(obj)->chipset == 0x11) {
220 nv_wrvgac(obj, 0, 0x2e, owner);
221 nv_wrvgac(obj, 0, 0x2e, owner);
222 }
223 } else
224 nv_error(obj, "wrvgaowner after nv4x\n");
225}
226