nouveau_nvkm_subdev_i2c_base.c revision 1.1
1/*	$NetBSD: nouveau_nvkm_subdev_i2c_base.c,v 1.1 2018/08/27 01:34:56 riastradh Exp $	*/
2
3/*
4 * Copyright 2013 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#include <sys/cdefs.h>
27__KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_subdev_i2c_base.c,v 1.1 2018/08/27 01:34:56 riastradh Exp $");
28
29#include "priv.h"
30#include "aux.h"
31#include "bus.h"
32#include "pad.h"
33
34#include <core/notify.h>
35#include <core/option.h>
36#include <subdev/bios.h>
37#include <subdev/bios/dcb.h>
38#include <subdev/bios/i2c.h>
39
40static struct nvkm_i2c_pad *
41nvkm_i2c_pad_find(struct nvkm_i2c *i2c, int id)
42{
43	struct nvkm_i2c_pad *pad;
44
45	list_for_each_entry(pad, &i2c->pad, head) {
46		if (pad->id == id)
47			return pad;
48	}
49
50	return NULL;
51}
52
53struct nvkm_i2c_bus *
54nvkm_i2c_bus_find(struct nvkm_i2c *i2c, int id)
55{
56	struct nvkm_bios *bios = i2c->subdev.device->bios;
57	struct nvkm_i2c_bus *bus;
58
59	if (id == NVKM_I2C_BUS_PRI || id == NVKM_I2C_BUS_SEC) {
60		u8  ver, hdr, cnt, len;
61		u16 i2c = dcb_i2c_table(bios, &ver, &hdr, &cnt, &len);
62		if (i2c && ver >= 0x30) {
63			u8 auxidx = nvbios_rd08(bios, i2c + 4);
64			if (id == NVKM_I2C_BUS_PRI)
65				id = NVKM_I2C_BUS_CCB((auxidx & 0x0f) >> 0);
66			else
67				id = NVKM_I2C_BUS_CCB((auxidx & 0xf0) >> 4);
68		} else {
69			id = NVKM_I2C_BUS_CCB(2);
70		}
71	}
72
73	list_for_each_entry(bus, &i2c->bus, head) {
74		if (bus->id == id)
75			return bus;
76	}
77
78	return NULL;
79}
80
81struct nvkm_i2c_aux *
82nvkm_i2c_aux_find(struct nvkm_i2c *i2c, int id)
83{
84	struct nvkm_i2c_aux *aux;
85
86	list_for_each_entry(aux, &i2c->aux, head) {
87		if (aux->id == id)
88			return aux;
89	}
90
91	return NULL;
92}
93
94static void
95nvkm_i2c_intr_fini(struct nvkm_event *event, int type, int id)
96{
97	struct nvkm_i2c *i2c = container_of(event, typeof(*i2c), event);
98	struct nvkm_i2c_aux *aux = nvkm_i2c_aux_find(i2c, id);
99	if (aux)
100		i2c->func->aux_mask(i2c, type, aux->intr, 0);
101}
102
103static void
104nvkm_i2c_intr_init(struct nvkm_event *event, int type, int id)
105{
106	struct nvkm_i2c *i2c = container_of(event, typeof(*i2c), event);
107	struct nvkm_i2c_aux *aux = nvkm_i2c_aux_find(i2c, id);
108	if (aux)
109		i2c->func->aux_mask(i2c, type, aux->intr, aux->intr);
110}
111
112static int
113nvkm_i2c_intr_ctor(struct nvkm_object *object, void *data, u32 size,
114		   struct nvkm_notify *notify)
115{
116	struct nvkm_i2c_ntfy_req *req = data;
117	if (!WARN_ON(size != sizeof(*req))) {
118		notify->size  = sizeof(struct nvkm_i2c_ntfy_rep);
119		notify->types = req->mask;
120		notify->index = req->port;
121		return 0;
122	}
123	return -EINVAL;
124}
125
126static const struct nvkm_event_func
127nvkm_i2c_intr_func = {
128	.ctor = nvkm_i2c_intr_ctor,
129	.init = nvkm_i2c_intr_init,
130	.fini = nvkm_i2c_intr_fini,
131};
132
133static void
134nvkm_i2c_intr(struct nvkm_subdev *subdev)
135{
136	struct nvkm_i2c *i2c = nvkm_i2c(subdev);
137	struct nvkm_i2c_aux *aux;
138	u32 hi, lo, rq, tx;
139
140	if (!i2c->func->aux_stat)
141		return;
142
143	i2c->func->aux_stat(i2c, &hi, &lo, &rq, &tx);
144	if (!hi && !lo && !rq && !tx)
145		return;
146
147	list_for_each_entry(aux, &i2c->aux, head) {
148		u32 mask = 0;
149		if (hi & aux->intr) mask |= NVKM_I2C_PLUG;
150		if (lo & aux->intr) mask |= NVKM_I2C_UNPLUG;
151		if (rq & aux->intr) mask |= NVKM_I2C_IRQ;
152		if (tx & aux->intr) mask |= NVKM_I2C_DONE;
153		if (mask) {
154			struct nvkm_i2c_ntfy_rep rep = {
155				.mask = mask,
156			};
157			nvkm_event_send(&i2c->event, rep.mask, aux->id,
158					&rep, sizeof(rep));
159		}
160	}
161}
162
163static int
164nvkm_i2c_fini(struct nvkm_subdev *subdev, bool suspend)
165{
166	struct nvkm_i2c *i2c = nvkm_i2c(subdev);
167	struct nvkm_i2c_pad *pad;
168	u32 mask;
169
170	if ((mask = (1 << i2c->func->aux) - 1), i2c->func->aux_stat) {
171		i2c->func->aux_mask(i2c, NVKM_I2C_ANY, mask, 0);
172		i2c->func->aux_stat(i2c, &mask, &mask, &mask, &mask);
173	}
174
175	list_for_each_entry(pad, &i2c->pad, head) {
176		nvkm_i2c_pad_fini(pad);
177	}
178
179	return 0;
180}
181
182static int
183nvkm_i2c_init(struct nvkm_subdev *subdev)
184{
185	struct nvkm_i2c *i2c = nvkm_i2c(subdev);
186	struct nvkm_i2c_bus *bus;
187	struct nvkm_i2c_pad *pad;
188
189	list_for_each_entry(pad, &i2c->pad, head) {
190		nvkm_i2c_pad_init(pad);
191	}
192
193	list_for_each_entry(bus, &i2c->bus, head) {
194		nvkm_i2c_bus_init(bus);
195	}
196
197	return 0;
198}
199
200static void *
201nvkm_i2c_dtor(struct nvkm_subdev *subdev)
202{
203	struct nvkm_i2c *i2c = nvkm_i2c(subdev);
204
205	nvkm_event_fini(&i2c->event);
206
207	while (!list_empty(&i2c->aux)) {
208		struct nvkm_i2c_aux *aux =
209			list_first_entry(&i2c->aux, typeof(*aux), head);
210		nvkm_i2c_aux_del(&aux);
211	}
212
213	while (!list_empty(&i2c->bus)) {
214		struct nvkm_i2c_bus *bus =
215			list_first_entry(&i2c->bus, typeof(*bus), head);
216		nvkm_i2c_bus_del(&bus);
217	}
218
219	while (!list_empty(&i2c->pad)) {
220		struct nvkm_i2c_pad *pad =
221			list_first_entry(&i2c->pad, typeof(*pad), head);
222		nvkm_i2c_pad_del(&pad);
223	}
224
225	return i2c;
226}
227
228static const struct nvkm_subdev_func
229nvkm_i2c = {
230	.dtor = nvkm_i2c_dtor,
231	.init = nvkm_i2c_init,
232	.fini = nvkm_i2c_fini,
233	.intr = nvkm_i2c_intr,
234};
235
236static const struct nvkm_i2c_drv {
237	u8 bios;
238	u8 addr;
239	int (*pad_new)(struct nvkm_i2c_bus *, int id, u8 addr,
240		       struct nvkm_i2c_pad **);
241}
242nvkm_i2c_drv[] = {
243	{ 0x0d, 0x39, anx9805_pad_new },
244	{ 0x0e, 0x3b, anx9805_pad_new },
245	{}
246};
247
248int
249nvkm_i2c_new_(const struct nvkm_i2c_func *func, struct nvkm_device *device,
250	      int index, struct nvkm_i2c **pi2c)
251{
252	struct nvkm_bios *bios = device->bios;
253	struct nvkm_i2c *i2c;
254	struct dcb_i2c_entry ccbE;
255	struct dcb_output dcbE;
256	u8 ver, hdr;
257	int ret, i;
258
259	if (!(i2c = *pi2c = kzalloc(sizeof(*i2c), GFP_KERNEL)))
260		return -ENOMEM;
261
262	nvkm_subdev_ctor(&nvkm_i2c, device, index, 0, &i2c->subdev);
263	i2c->func = func;
264	INIT_LIST_HEAD(&i2c->pad);
265	INIT_LIST_HEAD(&i2c->bus);
266	INIT_LIST_HEAD(&i2c->aux);
267
268	i = -1;
269	while (!dcb_i2c_parse(bios, ++i, &ccbE)) {
270		struct nvkm_i2c_pad *pad = NULL;
271		struct nvkm_i2c_bus *bus = NULL;
272		struct nvkm_i2c_aux *aux = NULL;
273
274		nvkm_debug(&i2c->subdev, "ccb %02x: type %02x drive %02x "
275			   "sense %02x share %02x auxch %02x\n", i, ccbE.type,
276			   ccbE.drive, ccbE.sense, ccbE.share, ccbE.auxch);
277
278		if (ccbE.share != DCB_I2C_UNUSED) {
279			const int id = NVKM_I2C_PAD_HYBRID(ccbE.share);
280			if (!(pad = nvkm_i2c_pad_find(i2c, id)))
281				ret = func->pad_s_new(i2c, id, &pad);
282			else
283				ret = 0;
284		} else {
285			ret = func->pad_x_new(i2c, NVKM_I2C_PAD_CCB(i), &pad);
286		}
287
288		if (ret) {
289			nvkm_error(&i2c->subdev, "ccb %02x pad, %d\n", i, ret);
290			nvkm_i2c_pad_del(&pad);
291			continue;
292		}
293
294		if (pad->func->bus_new_0 && ccbE.type == DCB_I2C_NV04_BIT) {
295			ret = pad->func->bus_new_0(pad, NVKM_I2C_BUS_CCB(i),
296						   ccbE.drive,
297						   ccbE.sense, &bus);
298		} else
299		if (pad->func->bus_new_4 &&
300		    ( ccbE.type == DCB_I2C_NV4E_BIT ||
301		      ccbE.type == DCB_I2C_NVIO_BIT ||
302		     (ccbE.type == DCB_I2C_PMGR &&
303		      ccbE.drive != DCB_I2C_UNUSED))) {
304			ret = pad->func->bus_new_4(pad, NVKM_I2C_BUS_CCB(i),
305						   ccbE.drive, &bus);
306		}
307
308		if (ret) {
309			nvkm_error(&i2c->subdev, "ccb %02x bus, %d\n", i, ret);
310			nvkm_i2c_bus_del(&bus);
311		}
312
313		if (pad->func->aux_new_6 &&
314		    ( ccbE.type == DCB_I2C_NVIO_AUX ||
315		     (ccbE.type == DCB_I2C_PMGR &&
316		      ccbE.auxch != DCB_I2C_UNUSED))) {
317			ret = pad->func->aux_new_6(pad, NVKM_I2C_BUS_CCB(i),
318						   ccbE.auxch, &aux);
319		} else {
320			ret = 0;
321		}
322
323		if (ret) {
324			nvkm_error(&i2c->subdev, "ccb %02x aux, %d\n", i, ret);
325			nvkm_i2c_aux_del(&aux);
326		}
327
328		if (ccbE.type != DCB_I2C_UNUSED && !bus && !aux) {
329			nvkm_warn(&i2c->subdev, "ccb %02x was ignored\n", i);
330			continue;
331		}
332	}
333
334	i = -1;
335	while (dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE)) {
336		const struct nvkm_i2c_drv *drv = nvkm_i2c_drv;
337		struct nvkm_i2c_bus *bus;
338		struct nvkm_i2c_pad *pad;
339
340		/* internal outputs handled by native i2c busses (above) */
341		if (!dcbE.location)
342			continue;
343
344		/* we need an i2c bus to talk to the external encoder */
345		bus = nvkm_i2c_bus_find(i2c, dcbE.i2c_index);
346		if (!bus) {
347			nvkm_debug(&i2c->subdev, "dcb %02x no bus\n", i);
348			continue;
349		}
350
351		/* ... and a driver for it */
352		while (drv->pad_new) {
353			if (drv->bios == dcbE.extdev)
354				break;
355			drv++;
356		}
357
358		if (!drv->pad_new) {
359			nvkm_debug(&i2c->subdev, "dcb %02x drv %02x unknown\n",
360				   i, dcbE.extdev);
361			continue;
362		}
363
364		/* find/create an instance of the driver */
365		pad = nvkm_i2c_pad_find(i2c, NVKM_I2C_PAD_EXT(dcbE.extdev));
366		if (!pad) {
367			const int id = NVKM_I2C_PAD_EXT(dcbE.extdev);
368			ret = drv->pad_new(bus, id, drv->addr, &pad);
369			if (ret) {
370				nvkm_error(&i2c->subdev, "dcb %02x pad, %d\n",
371					   i, ret);
372				nvkm_i2c_pad_del(&pad);
373				continue;
374			}
375		}
376
377		/* create any i2c bus / aux channel required by the output */
378		if (pad->func->aux_new_6 && dcbE.type == DCB_OUTPUT_DP) {
379			const int id = NVKM_I2C_AUX_EXT(dcbE.extdev);
380			struct nvkm_i2c_aux *aux = NULL;
381			ret = pad->func->aux_new_6(pad, id, 0, &aux);
382			if (ret) {
383				nvkm_error(&i2c->subdev, "dcb %02x aux, %d\n",
384					   i, ret);
385				nvkm_i2c_aux_del(&aux);
386			}
387		} else
388		if (pad->func->bus_new_4) {
389			const int id = NVKM_I2C_BUS_EXT(dcbE.extdev);
390			struct nvkm_i2c_bus *bus = NULL;
391			ret = pad->func->bus_new_4(pad, id, 0, &bus);
392			if (ret) {
393				nvkm_error(&i2c->subdev, "dcb %02x bus, %d\n",
394					   i, ret);
395				nvkm_i2c_bus_del(&bus);
396			}
397		}
398	}
399
400	return nvkm_event_init(&nvkm_i2c_intr_func, 4, i, &i2c->event);
401}
402