brintos

brintos / linux-shallow public Read only

0
0
Text · 10.1 KiB · 731b2f6 Raw
418 lines · c
1/*2 * Copyright 2013 Red Hat Inc.3 *4 * Permission is hereby granted, free of charge, to any person obtaining a5 * copy of this software and associated documentation files (the "Software"),6 * to deal in the Software without restriction, including without limitation7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,8 * and/or sell copies of the Software, and to permit persons to whom the9 * Software is furnished to do so, subject to the following conditions:10 *11 * The above copyright notice and this permission notice shall be included in12 * all copies or substantial portions of the Software.13 *14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR20 * OTHER DEALINGS IN THE SOFTWARE.21 *22 * Authors: Ben Skeggs23 */24#include "priv.h"25#include "aux.h"26#include "bus.h"27#include "pad.h"28 29#include <core/option.h>30#include <subdev/bios.h>31#include <subdev/bios/dcb.h>32#include <subdev/bios/i2c.h>33 34static struct nvkm_i2c_pad *35nvkm_i2c_pad_find(struct nvkm_i2c *i2c, int id)36{37	struct nvkm_i2c_pad *pad;38 39	list_for_each_entry(pad, &i2c->pad, head) {40		if (pad->id == id)41			return pad;42	}43 44	return NULL;45}46 47struct nvkm_i2c_bus *48nvkm_i2c_bus_find(struct nvkm_i2c *i2c, int id)49{50	struct nvkm_bios *bios = i2c->subdev.device->bios;51	struct nvkm_i2c_bus *bus;52 53	if (id == NVKM_I2C_BUS_PRI || id == NVKM_I2C_BUS_SEC) {54		u8  ver, hdr, cnt, len;55		u16 i2c = dcb_i2c_table(bios, &ver, &hdr, &cnt, &len);56		if (i2c && ver >= 0x30) {57			u8 auxidx = nvbios_rd08(bios, i2c + 4);58			if (id == NVKM_I2C_BUS_PRI)59				id = NVKM_I2C_BUS_CCB((auxidx & 0x0f) >> 0);60			else61				id = NVKM_I2C_BUS_CCB((auxidx & 0xf0) >> 4);62		} else {63			id = NVKM_I2C_BUS_CCB(2);64		}65	}66 67	list_for_each_entry(bus, &i2c->bus, head) {68		if (bus->id == id)69			return bus;70	}71 72	return NULL;73}74 75struct nvkm_i2c_aux *76nvkm_i2c_aux_find(struct nvkm_i2c *i2c, int id)77{78	struct nvkm_i2c_aux *aux;79 80	list_for_each_entry(aux, &i2c->aux, head) {81		if (aux->id == id)82			return aux;83	}84 85	return NULL;86}87 88static void89nvkm_i2c_intr_fini(struct nvkm_event *event, int type, int id)90{91	struct nvkm_i2c *i2c = container_of(event, typeof(*i2c), event);92	struct nvkm_i2c_aux *aux = nvkm_i2c_aux_find(i2c, id);93	if (aux)94		i2c->func->aux_mask(i2c, type, aux->intr, 0);95}96 97static void98nvkm_i2c_intr_init(struct nvkm_event *event, int type, int id)99{100	struct nvkm_i2c *i2c = container_of(event, typeof(*i2c), event);101	struct nvkm_i2c_aux *aux = nvkm_i2c_aux_find(i2c, id);102	if (aux)103		i2c->func->aux_mask(i2c, type, aux->intr, aux->intr);104}105 106static const struct nvkm_event_func107nvkm_i2c_intr_func = {108	.init = nvkm_i2c_intr_init,109	.fini = nvkm_i2c_intr_fini,110};111 112static void113nvkm_i2c_intr(struct nvkm_subdev *subdev)114{115	struct nvkm_i2c *i2c = nvkm_i2c(subdev);116	struct nvkm_i2c_aux *aux;117	u32 hi, lo, rq, tx;118 119	if (!i2c->func->aux_stat)120		return;121 122	i2c->func->aux_stat(i2c, &hi, &lo, &rq, &tx);123	if (!hi && !lo && !rq && !tx)124		return;125 126	list_for_each_entry(aux, &i2c->aux, head) {127		u32 mask = 0;128		if (hi & aux->intr) mask |= NVKM_I2C_PLUG;129		if (lo & aux->intr) mask |= NVKM_I2C_UNPLUG;130		if (rq & aux->intr) mask |= NVKM_I2C_IRQ;131		if (tx & aux->intr) mask |= NVKM_I2C_DONE;132		if (mask)133			nvkm_event_ntfy(&i2c->event, aux->id, mask);134	}135}136 137static int138nvkm_i2c_fini(struct nvkm_subdev *subdev, bool suspend)139{140	struct nvkm_i2c *i2c = nvkm_i2c(subdev);141	struct nvkm_i2c_pad *pad;142	struct nvkm_i2c_bus *bus;143	struct nvkm_i2c_aux *aux;144	u32 mask;145 146	list_for_each_entry(aux, &i2c->aux, head) {147		nvkm_i2c_aux_fini(aux);148	}149 150	list_for_each_entry(bus, &i2c->bus, head) {151		nvkm_i2c_bus_fini(bus);152	}153 154	if ((mask = (1 << i2c->func->aux) - 1), i2c->func->aux_stat) {155		i2c->func->aux_mask(i2c, NVKM_I2C_ANY, mask, 0);156		i2c->func->aux_stat(i2c, &mask, &mask, &mask, &mask);157	}158 159	list_for_each_entry(pad, &i2c->pad, head) {160		nvkm_i2c_pad_fini(pad);161	}162 163	return 0;164}165 166static int167nvkm_i2c_preinit(struct nvkm_subdev *subdev)168{169	struct nvkm_i2c *i2c = nvkm_i2c(subdev);170	struct nvkm_i2c_bus *bus;171	struct nvkm_i2c_pad *pad;172 173	/*174	 * We init our i2c busses as early as possible, since they may be175	 * needed by the vbios init scripts on some cards176	 */177	list_for_each_entry(pad, &i2c->pad, head)178		nvkm_i2c_pad_init(pad);179	list_for_each_entry(bus, &i2c->bus, head)180		nvkm_i2c_bus_init(bus);181 182	return 0;183}184 185static int186nvkm_i2c_init(struct nvkm_subdev *subdev)187{188	struct nvkm_i2c *i2c = nvkm_i2c(subdev);189	struct nvkm_i2c_bus *bus;190	struct nvkm_i2c_pad *pad;191	struct nvkm_i2c_aux *aux;192 193	list_for_each_entry(pad, &i2c->pad, head) {194		nvkm_i2c_pad_init(pad);195	}196 197	list_for_each_entry(bus, &i2c->bus, head) {198		nvkm_i2c_bus_init(bus);199	}200 201	list_for_each_entry(aux, &i2c->aux, head) {202		nvkm_i2c_aux_init(aux);203	}204 205	return 0;206}207 208static void *209nvkm_i2c_dtor(struct nvkm_subdev *subdev)210{211	struct nvkm_i2c *i2c = nvkm_i2c(subdev);212 213	nvkm_event_fini(&i2c->event);214 215	while (!list_empty(&i2c->aux)) {216		struct nvkm_i2c_aux *aux =217			list_first_entry(&i2c->aux, typeof(*aux), head);218		nvkm_i2c_aux_del(&aux);219	}220 221	while (!list_empty(&i2c->bus)) {222		struct nvkm_i2c_bus *bus =223			list_first_entry(&i2c->bus, typeof(*bus), head);224		nvkm_i2c_bus_del(&bus);225	}226 227	while (!list_empty(&i2c->pad)) {228		struct nvkm_i2c_pad *pad =229			list_first_entry(&i2c->pad, typeof(*pad), head);230		nvkm_i2c_pad_del(&pad);231	}232 233	return i2c;234}235 236static const struct nvkm_subdev_func237nvkm_i2c = {238	.dtor = nvkm_i2c_dtor,239	.preinit = nvkm_i2c_preinit,240	.init = nvkm_i2c_init,241	.fini = nvkm_i2c_fini,242	.intr = nvkm_i2c_intr,243};244 245static const struct nvkm_i2c_drv {246	u8 bios;247	u8 addr;248	int (*pad_new)(struct nvkm_i2c_bus *, int id, u8 addr,249		       struct nvkm_i2c_pad **);250}251nvkm_i2c_drv[] = {252	{ 0x0d, 0x39, anx9805_pad_new },253	{ 0x0e, 0x3b, anx9805_pad_new },254	{}255};256 257int258nvkm_i2c_new_(const struct nvkm_i2c_func *func, struct nvkm_device *device,259	      enum nvkm_subdev_type type, int inst, struct nvkm_i2c **pi2c)260{261	struct nvkm_bios *bios = device->bios;262	struct nvkm_i2c *i2c;263	struct nvkm_i2c_aux *aux;264	struct dcb_i2c_entry ccbE;265	struct dcb_output dcbE;266	u8 ver, hdr;267	int ret, i, ids;268 269	if (!(i2c = *pi2c = kzalloc(sizeof(*i2c), GFP_KERNEL)))270		return -ENOMEM;271 272	nvkm_subdev_ctor(&nvkm_i2c, device, type, inst, &i2c->subdev);273	i2c->func = func;274	INIT_LIST_HEAD(&i2c->pad);275	INIT_LIST_HEAD(&i2c->bus);276	INIT_LIST_HEAD(&i2c->aux);277 278	i = -1;279	while (!dcb_i2c_parse(bios, ++i, &ccbE)) {280		struct nvkm_i2c_pad *pad = NULL;281		struct nvkm_i2c_bus *bus = NULL;282		struct nvkm_i2c_aux *aux = NULL;283 284		nvkm_debug(&i2c->subdev, "ccb %02x: type %02x drive %02x "285			   "sense %02x share %02x auxch %02x\n", i, ccbE.type,286			   ccbE.drive, ccbE.sense, ccbE.share, ccbE.auxch);287 288		if (ccbE.share != DCB_I2C_UNUSED) {289			const int id = NVKM_I2C_PAD_HYBRID(ccbE.share);290			if (!(pad = nvkm_i2c_pad_find(i2c, id)))291				ret = func->pad_s_new(i2c, id, &pad);292			else293				ret = 0;294		} else {295			ret = func->pad_x_new(i2c, NVKM_I2C_PAD_CCB(i), &pad);296		}297 298		if (ret) {299			nvkm_error(&i2c->subdev, "ccb %02x pad, %d\n", i, ret);300			nvkm_i2c_pad_del(&pad);301			continue;302		}303 304		if (pad->func->bus_new_0 && ccbE.type == DCB_I2C_NV04_BIT) {305			ret = pad->func->bus_new_0(pad, NVKM_I2C_BUS_CCB(i),306						   ccbE.drive,307						   ccbE.sense, &bus);308		} else309		if (pad->func->bus_new_4 &&310		    ( ccbE.type == DCB_I2C_NV4E_BIT ||311		      ccbE.type == DCB_I2C_NVIO_BIT ||312		     (ccbE.type == DCB_I2C_PMGR &&313		      ccbE.drive != DCB_I2C_UNUSED))) {314			ret = pad->func->bus_new_4(pad, NVKM_I2C_BUS_CCB(i),315						   ccbE.drive, &bus);316		}317 318		if (ret) {319			nvkm_error(&i2c->subdev, "ccb %02x bus, %d\n", i, ret);320			nvkm_i2c_bus_del(&bus);321		}322 323		if (pad->func->aux_new_6 &&324		    ( ccbE.type == DCB_I2C_NVIO_AUX ||325		     (ccbE.type == DCB_I2C_PMGR &&326		      ccbE.auxch != DCB_I2C_UNUSED))) {327			ret = pad->func->aux_new_6(pad, NVKM_I2C_BUS_CCB(i),328						   ccbE.auxch, &aux);329		} else {330			ret = 0;331		}332 333		if (ret) {334			nvkm_error(&i2c->subdev, "ccb %02x aux, %d\n", i, ret);335			nvkm_i2c_aux_del(&aux);336		}337 338		if (ccbE.type != DCB_I2C_UNUSED && !bus && !aux) {339			nvkm_warn(&i2c->subdev, "ccb %02x was ignored\n", i);340			continue;341		}342	}343 344	i = -1;345	while (dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE)) {346		const struct nvkm_i2c_drv *drv = nvkm_i2c_drv;347		struct nvkm_i2c_bus *bus;348		struct nvkm_i2c_pad *pad;349 350		/* internal outputs handled by native i2c busses (above) */351		if (!dcbE.location)352			continue;353 354		/* we need an i2c bus to talk to the external encoder */355		bus = nvkm_i2c_bus_find(i2c, dcbE.i2c_index);356		if (!bus) {357			nvkm_debug(&i2c->subdev, "dcb %02x no bus\n", i);358			continue;359		}360 361		/* ... and a driver for it */362		while (drv->pad_new) {363			if (drv->bios == dcbE.extdev)364				break;365			drv++;366		}367 368		if (!drv->pad_new) {369			nvkm_debug(&i2c->subdev, "dcb %02x drv %02x unknown\n",370				   i, dcbE.extdev);371			continue;372		}373 374		/* find/create an instance of the driver */375		pad = nvkm_i2c_pad_find(i2c, NVKM_I2C_PAD_EXT(dcbE.extdev));376		if (!pad) {377			const int id = NVKM_I2C_PAD_EXT(dcbE.extdev);378			ret = drv->pad_new(bus, id, drv->addr, &pad);379			if (ret) {380				nvkm_error(&i2c->subdev, "dcb %02x pad, %d\n",381					   i, ret);382				nvkm_i2c_pad_del(&pad);383				continue;384			}385		}386 387		/* create any i2c bus / aux channel required by the output */388		if (pad->func->aux_new_6 && dcbE.type == DCB_OUTPUT_DP) {389			const int id = NVKM_I2C_AUX_EXT(dcbE.extdev);390			struct nvkm_i2c_aux *aux = NULL;391			ret = pad->func->aux_new_6(pad, id, 0, &aux);392			if (ret) {393				nvkm_error(&i2c->subdev, "dcb %02x aux, %d\n",394					   i, ret);395				nvkm_i2c_aux_del(&aux);396			}397		} else398		if (pad->func->bus_new_4) {399			const int id = NVKM_I2C_BUS_EXT(dcbE.extdev);400			struct nvkm_i2c_bus *bus = NULL;401			ret = pad->func->bus_new_4(pad, id, 0, &bus);402			if (ret) {403				nvkm_error(&i2c->subdev, "dcb %02x bus, %d\n",404					   i, ret);405				nvkm_i2c_bus_del(&bus);406			}407		}408	}409 410	ids = 0;411	list_for_each_entry(aux, &i2c->aux, head)412		ids = max(ids, aux->id + 1);413	if (!ids)414		return 0;415 416	return nvkm_event_init(&nvkm_i2c_intr_func, &i2c->subdev, 4, ids, &i2c->event);417}418