diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index e12b4ff..fd75226 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -19,7 +19,8 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nv50_crtc.o nv50_dac.o nv50_sor.o \ nv50_cursor.o nv50_display.o nv50_fbcon.o \ nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \ - nv04_crtc.o nv04_display.o nv04_cursor.o nv04_fbcon.o + nv04_crtc.o nv04_display.o nv04_cursor.o nv04_fbcon.o \ + nv17_gpio.o nouveau-$(CONFIG_DRM_NOUVEAU_DEBUG) += nouveau_debugfs.o nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 1079508..2ee96fa 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -2550,22 +2550,25 @@ init_8e(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) * cleared. */ - uint8_t headerlen = bios->data[bios->bdcb.init8e_table_ptr + 1]; - uint8_t entries = bios->data[bios->bdcb.init8e_table_ptr + 2]; - uint8_t recordlen = bios->data[bios->bdcb.init8e_table_ptr + 3]; + uint8_t *gpio_table = bios->bdcb.gpio_table; + uint8_t headerlen, entries, recordlen; int i; if (bios->bdcb.version != 0x40) { NV_ERROR(bios->dev, "DCB table not version 4.0\n"); return false; } - if (!bios->bdcb.init8e_table_ptr) { - NV_WARN(bios->dev, "Invalid pointer to INIT_8E table\n"); + if (!gpio_table) { + NV_WARN(bios->dev, "Invalid pointer to DCB GPIO table\n"); return false; } + headerlen = gpio_table[1]; + entries = gpio_table[2]; + recordlen = gpio_table[3]; + for (i = 0; i < entries; i++) { - uint32_t entry = ROM32(bios->data[bios->bdcb.init8e_table_ptr + headerlen + recordlen * i]); + uint32_t entry = ROM32(gpio_table[headerlen + recordlen * i]); int shift = (entry & 0x1f) * 4; uint32_t mask; uint32_t reg = 0xe104; @@ -4965,6 +4968,112 @@ read_dcb_i2c_entry(struct drm_device *dev, int dcb_version, uint8_t *i2ctable, i return 0; } +static struct dcb_gpio_entry * +new_gpio_entry(struct nvbios *bios) +{ + struct parsed_dcb_gpio *gpio = &bios->bdcb.gpio; + + return &gpio->entry[gpio->entries++]; +} + +static void +parse_dcb30_gpio_entry(struct nvbios *bios, int offset) +{ + struct dcb_gpio_entry *gpio; + uint16_t ent = ROM16(bios->bdcb.gpio_table[offset]); + uint8_t line = ent & 0x1f, + tag = ent >> 5 & 0x3f, + flags = ent >> 11 & 0x1f; + + if (tag == 0x3f) + return; + + gpio = new_gpio_entry(bios); + + gpio->tag = tag; + gpio->line = line; + gpio->invert = flags != 4; +} + +static void +parse_dcb40_gpio_entry(struct nvbios *bios, int offset) +{ + struct dcb_gpio_entry *gpio; + uint32_t ent = ROM32(bios->bdcb.gpio_table[offset]); + uint8_t line = ent & 0x1f, + tag = ent >> 8 & 0xff; + + if (tag == 0xff) + return; + + gpio = new_gpio_entry(bios); + + /* Currently unused, we may need more fields parsed at some + * point. */ + gpio->tag = tag; + gpio->line = line; +} + +static void +parse_dcb_gpio_table(struct drm_device *dev, struct nvbios *bios) +{ + uint8_t *gpio_table = bios->bdcb.gpio_table; + int header_len, entries, entry_len; + void (*parse_entry)(struct nvbios *, int); + int i; + + if (gpio_table) { + header_len = gpio_table[1]; + entries = gpio_table[2]; + entry_len = gpio_table[3]; + } + + if (bios->bdcb.version >= 0x40) { + if (gpio_table && entry_len != 4) { + NV_WARN(dev, "Invalid DCB GPIO table entry length.\n"); + return; + } + + parse_entry = parse_dcb40_gpio_entry; + + } else if (bios->bdcb.version >= 0x30) { + if (gpio_table && entry_len != 2) { + NV_WARN(dev, "Invalid DCB GPIO table entry length.\n"); + return; + } + + parse_entry = parse_dcb30_gpio_entry; + + } else if (bios->bdcb.version >= 0x22) { + /* + * DCBs older than v3.0 don't really have a GPIO + * table, instead they keep some GPIO info at fixed + * locations. + */ + uint16_t dcbptr = ROM16(bios->data[0x36]); + uint8_t *tvdac_gpio = &bios->data[dcbptr - 5]; + + if (tvdac_gpio[0] & 1) { + struct dcb_gpio_entry *gpio = new_gpio_entry(bios); + + gpio->tag = DCB_GPIO_TVDAC0; + gpio->line = tvdac_gpio[1] >> 4; + gpio->invert = tvdac_gpio[0] & 2; + } + } + + if (!gpio_table) + return; + + if (entries > DCB_MAX_NUM_GPIO_ENTRIES) { + NV_WARN(dev, "Too many entries in the DCB GPIO table.\n"); + entries = DCB_MAX_NUM_GPIO_ENTRIES; + } + + for (i = 0; i < entries; i++) + parse_entry(bios, header_len + entry_len * i); +} + static struct dcb_entry *new_dcb_entry(struct parsed_dcb *dcb) { struct dcb_entry *entry = &dcb->entry[dcb->entries]; @@ -5294,9 +5403,8 @@ static int parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool two recordlength = dcbtable[3]; i2ctabptr = ROM16(dcbtable[4]); sig = ROM32(dcbtable[6]); - if (bdcb->version == 0x40) /* G80 */ - bdcb->init8e_table_ptr = - ROM16(dcbtable[10]); + bdcb->gpio_table = &bios->data[ROM16(dcbtable[10])]; + } else { i2ctabptr = ROM16(dcbtable[2]); sig = ROM32(dcbtable[4]); @@ -5361,6 +5469,8 @@ static int parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool two bdcb->i2c_default_indices = bdcb->i2c_table[4]; } + parse_dcb_gpio_table(dev, bios); + if (entries > DCB_MAX_NUM_ENTRIES) entries = DCB_MAX_NUM_ENTRIES; diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index 1ffda97..d131ef9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -29,6 +29,7 @@ #define DCB_MAX_NUM_ENTRIES 16 #define DCB_MAX_NUM_I2C_ENTRIES 16 +#define DCB_MAX_NUM_GPIO_ENTRIES 32 #define DCB_LOC_ON_CHIP 0 @@ -69,14 +70,32 @@ struct parsed_dcb { struct dcb_i2c_entry i2c[DCB_MAX_NUM_I2C_ENTRIES]; }; +enum dcb_gpio_tag { + DCB_GPIO_TVDAC0 = 0xc, + DCB_GPIO_TVDAC1 = 0x2d, +}; + +struct dcb_gpio_entry { + enum dcb_gpio_tag tag; + int line; + bool invert; +}; + +struct parsed_dcb_gpio { + int entries; + struct dcb_gpio_entry entry[DCB_MAX_NUM_GPIO_ENTRIES]; +}; + struct bios_parsed_dcb { uint8_t version; struct parsed_dcb dcb; - uint16_t init8e_table_ptr; uint8_t *i2c_table; uint8_t i2c_default_indices; + + uint8_t *gpio_table; + struct parsed_dcb_gpio gpio; }; enum nouveau_encoder_type { diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index e33fdd3..f8c3143 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -1092,6 +1092,10 @@ extern int nouveau_gem_ioctl_cpu_fini(struct drm_device *, void *, extern int nouveau_gem_ioctl_info(struct drm_device *, void *, struct drm_file *); +/* nv17_gpio.c */ +int nv17_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag); +int nv17_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state); + #ifndef ioread32_native #ifdef __BIG_ENDIAN #define ioread16_native ioread16be diff --git a/drivers/gpu/drm/nouveau/nv04_dac.c b/drivers/gpu/drm/nouveau/nv04_dac.c index 587b6f5..97ee710 100644 --- a/drivers/gpu/drm/nouveau/nv04_dac.c +++ b/drivers/gpu/drm/nouveau/nv04_dac.c @@ -220,7 +220,7 @@ enum drm_connector_status nv17_dac_detect(struct drm_encoder *encoder, struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; uint32_t testval, regoffset = nv04_dac_output_offset(encoder); uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput, - saved_rtest_ctrl, temp, saved_gpio_ext = 0, routput; + saved_rtest_ctrl, saved_gpio0, saved_gpio1, temp, routput; int head, present = 0; #define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20) @@ -248,12 +248,11 @@ enum drm_connector_status nv17_dac_detect(struct drm_encoder *encoder, nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf); } - if (dev_priv->chipset >= 0x34) { - saved_gpio_ext = NVReadCRTC(dev, 0, NV_PCRTC_GPIO_EXT); + saved_gpio1 = nv17_gpio_get(dev, DCB_GPIO_TVDAC1); + saved_gpio0 = nv17_gpio_get(dev, DCB_GPIO_TVDAC0); - NVWriteCRTC(dev, 0, NV_PCRTC_GPIO_EXT, (saved_gpio_ext & ~(3 << 20)) | - (dcb->type == OUTPUT_TV ? (1 << 20) : 0)); - } + nv17_gpio_set(dev, DCB_GPIO_TVDAC1, dcb->type == OUTPUT_TV); + nv17_gpio_set(dev, DCB_GPIO_TVDAC0, dcb->type == OUTPUT_TV); msleep(4); @@ -291,7 +290,7 @@ enum drm_connector_status nv17_dac_detect(struct drm_encoder *encoder, temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); if (dcb->type == OUTPUT_TV) - present = (nv17_tv_detect(encoder, connector, (temp >> 28) & 0xe) + present = (nv17_tv_detect(encoder, connector, temp) == connector_status_connected); else present = temp & NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI; @@ -308,8 +307,8 @@ enum drm_connector_status nv17_dac_detect(struct drm_encoder *encoder, nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4); nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2); - if (dev_priv->chipset >= 0x34) - NVWriteRAMDAC(dev, 0, NV_PCRTC_GPIO_EXT, saved_gpio_ext); + nv17_gpio_set(dev, DCB_GPIO_TVDAC1, saved_gpio1); + nv17_gpio_set(dev, DCB_GPIO_TVDAC0, saved_gpio0); if (present) { NV_INFO(dev, "Load detected on output %c\n", '@' + ffs(dcb->or)); diff --git a/drivers/gpu/drm/nouveau/nv17_gpio.c b/drivers/gpu/drm/nouveau/nv17_gpio.c new file mode 100644 index 0000000..6ff5290 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv17_gpio.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2009 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "drmP.h" +#include "nouveau_drv.h" + +static struct dcb_gpio_entry * +get_gpio_entry(struct drm_device *dev, enum dcb_gpio_tag tag) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct parsed_dcb_gpio *gpio = &dev_priv->VBIOS.bdcb.gpio; + int i; + + for (i = 0; i < gpio->entries; i++) { + if (gpio->entry[i].tag == tag) + return &gpio->entry[i]; + } + + return NULL; +} + +static bool +get_gpio_location(struct dcb_gpio_entry *ent, uint32_t *reg, uint32_t *shift, + uint32_t *mask) +{ + if (ent->line < 2) { + *reg = NV_PCRTC_GPIO; + *shift = ent->line * 16; + *mask = 0x11; + + } else if (ent->line < 10) { + *reg = NV_PCRTC_GPIO_EXT; + *shift = (ent->line - 2) * 4; + *mask = 0x3; + + } else if (ent->line < 14) { + *reg = NV_PCRTC_850; + *shift = (ent->line - 10) * 4; + *mask = 0x3; + + } else { + return false; + } + + return true; +} + +int +nv17_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag) +{ + struct dcb_gpio_entry *ent = get_gpio_entry(dev, tag); + uint32_t reg, shift, mask, value; + + if (!ent) + return -ENODEV; + + if (!get_gpio_location(ent, ®, &shift, &mask)) + return -ENODEV; + + value = nv_rd32(dev, reg) >> shift; + + return (ent->invert ? 1 : 0) ^ (value & 1); +} + +int +nv17_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state) +{ + struct dcb_gpio_entry *ent = get_gpio_entry(dev, tag); + uint32_t reg, shift, mask, value; + + if (!ent) + return -ENODEV; + + if (!get_gpio_location(ent, ®, &shift, &mask)) + return -ENODEV; + + value = ((ent->invert ? 1 : 0) ^ (state ? 1 : 0)) << shift; + mask = ~(mask << shift); + + nv_wr32(dev, reg, value | (nv_rd32(dev, reg) & mask)); + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nv17_tv.c b/drivers/gpu/drm/nouveau/nv17_tv.c index 34f95c7..ad01456 100644 --- a/drivers/gpu/drm/nouveau/nv17_tv.c +++ b/drivers/gpu/drm/nouveau/nv17_tv.c @@ -39,9 +39,9 @@ enum drm_connector_status nv17_tv_detect(struct drm_encoder *encoder, { struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); - tv_enc->pin_mask = pin_mask; + tv_enc->pin_mask = pin_mask >> 28 & 0xe; - switch (pin_mask) { + switch (tv_enc->pin_mask) { case 0x2: case 0x4: tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Composite; @@ -212,7 +212,6 @@ static bool nv17_tv_mode_fixup(struct drm_encoder *encoder, static void nv17_tv_dpms(struct drm_encoder *encoder, int mode) { struct drm_device *dev = encoder->dev; - struct drm_nouveau_private *dev_priv = dev->dev_private; struct nv17_tv_state *regs = &to_tv_enc(encoder)->state; struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); @@ -237,15 +236,8 @@ static void nv17_tv_dpms(struct drm_encoder *encoder, int mode) nv_load_ptv(dev, regs, 200); - if (dev_priv->chipset >= 0x34) { - uint32_t *gpio_ext = &dev_priv->mode_reg.crtc_reg[0].gpio_ext; - - *gpio_ext &= ~(3 << 20); - if (mode == DRM_MODE_DPMS_ON) - *gpio_ext |= 1 << 20; - - NVWriteCRTC(dev, 0, NV_PCRTC_GPIO_EXT, *gpio_ext); - } + nv17_gpio_set(dev, DCB_GPIO_TVDAC1, mode == DRM_MODE_DPMS_ON); + nv17_gpio_set(dev, DCB_GPIO_TVDAC0, mode == DRM_MODE_DPMS_ON); nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON); }