diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau')
95 files changed, 4567 insertions, 4064 deletions
diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.h b/drivers/gpu/drm/nouveau/dispnv04/disp.h index bea4543554ba..74a8795c2c2b 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/disp.h +++ b/drivers/gpu/drm/nouveau/dispnv04/disp.h @@ -169,18 +169,10 @@ static inline void nouveau_bios_run_init_table(struct drm_device *dev, u16 table, struct dcb_output *outp, int crtc) { - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_bios *bios = nvxx_bios(&drm->client.device); - struct nvbios_init init = { - .subdev = &bios->subdev, - .bios = bios, - .offset = table, - .outp = outp, - .crtc = crtc, - .execute = 1, - }; - - nvbios_exec(&init); + nvbios_init(&nvxx_bios(&nouveau_drm(dev)->client.device)->subdev, table, + init.outp = outp; + init.head = crtc; + ); } #endif diff --git a/drivers/gpu/drm/nouveau/include/nvif/cl5070.h b/drivers/gpu/drm/nouveau/include/nvif/cl5070.h index ae49dfd1f97b..542d95145a67 100644 --- a/drivers/gpu/drm/nouveau/include/nvif/cl5070.h +++ b/drivers/gpu/drm/nouveau/include/nvif/cl5070.h @@ -27,29 +27,25 @@ struct nv50_disp_scanoutpos_v0 { struct nv50_disp_mthd_v1 { __u8 version; -#define NV50_DISP_MTHD_V1_DAC_PWR 0x10 +#define NV50_DISP_MTHD_V1_ACQUIRE 0x01 +#define NV50_DISP_MTHD_V1_RELEASE 0x02 #define NV50_DISP_MTHD_V1_DAC_LOAD 0x11 -#define NV50_DISP_MTHD_V1_SOR_PWR 0x20 #define NV50_DISP_MTHD_V1_SOR_HDA_ELD 0x21 #define NV50_DISP_MTHD_V1_SOR_HDMI_PWR 0x22 #define NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT 0x23 -#define NV50_DISP_MTHD_V1_SOR_DP_PWR 0x24 #define NV50_DISP_MTHD_V1_SOR_DP_MST_LINK 0x25 #define NV50_DISP_MTHD_V1_SOR_DP_MST_VCPI 0x26 -#define NV50_DISP_MTHD_V1_PIOR_PWR 0x30 __u8 method; __u16 hasht; __u16 hashm; __u8 pad06[2]; }; -struct nv50_disp_dac_pwr_v0 { +struct nv50_disp_acquire_v0 { __u8 version; - __u8 state; - __u8 data; - __u8 vsync; - __u8 hsync; - __u8 pad05[3]; + __u8 or; + __u8 link; + __u8 pad03[5]; }; struct nv50_disp_dac_load_v0 { @@ -59,12 +55,6 @@ struct nv50_disp_dac_load_v0 { __u32 data; }; -struct nv50_disp_sor_pwr_v0 { - __u8 version; - __u8 state; - __u8 pad02[6]; -}; - struct nv50_disp_sor_hda_eld_v0 { __u8 version; __u8 pad01[7]; @@ -76,7 +66,9 @@ struct nv50_disp_sor_hdmi_pwr_v0 { __u8 state; __u8 max_ac_packet; __u8 rekey; - __u8 pad04[4]; + __u8 avi_infoframe_length; + __u8 vendor_infoframe_length; + __u8 pad06[2]; }; struct nv50_disp_sor_lvds_script_v0 { @@ -86,12 +78,6 @@ struct nv50_disp_sor_lvds_script_v0 { __u8 pad04[4]; }; -struct nv50_disp_sor_dp_pwr_v0 { - __u8 version; - __u8 state; - __u8 pad02[6]; -}; - struct nv50_disp_sor_dp_mst_link_v0 { __u8 version; __u8 state; @@ -106,11 +92,4 @@ struct nv50_disp_sor_dp_mst_vcpi_v0 { __u16 pbn; __u16 aligned_pbn; }; - -struct nv50_disp_pior_pwr_v0 { - __u8 version; - __u8 state; - __u8 type; - __u8 pad03[5]; -}; #endif diff --git a/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h b/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h index 970ae753968a..05f9c13ab8c3 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h @@ -8,17 +8,15 @@ struct nvkm_disp { const struct nvkm_disp_func *func; struct nvkm_engine engine; - struct nvkm_oproxy *client; - + struct list_head head; + struct list_head ior; struct list_head outp; struct list_head conn; struct nvkm_event hpd; struct nvkm_event vblank; - struct { - int nr; - } head; + struct nvkm_oproxy *client; }; int nv04_disp_new(struct nvkm_device *, int, struct nvkm_disp **); @@ -26,7 +24,9 @@ int nv50_disp_new(struct nvkm_device *, int, struct nvkm_disp **); int g84_disp_new(struct nvkm_device *, int, struct nvkm_disp **); int gt200_disp_new(struct nvkm_device *, int, struct nvkm_disp **); int g94_disp_new(struct nvkm_device *, int, struct nvkm_disp **); +int mcp77_disp_new(struct nvkm_device *, int, struct nvkm_disp **); int gt215_disp_new(struct nvkm_device *, int, struct nvkm_disp **); +int mcp89_disp_new(struct nvkm_device *, int, struct nvkm_disp **); int gf119_disp_new(struct nvkm_device *, int, struct nvkm_disp **); int gk104_disp_new(struct nvkm_device *, int, struct nvkm_disp **); int gk110_disp_new(struct nvkm_device *, int, struct nvkm_disp **); diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/init.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/init.h index 4dc1c8af840c..06ab48052128 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/init.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/init.h @@ -3,19 +3,34 @@ struct nvbios_init { struct nvkm_subdev *subdev; - struct nvkm_bios *bios; - u16 offset; + u32 offset; + struct dcb_output *outp; - int crtc; + int or; + int link; + int head; /* internal state used during parsing */ u8 execute; u32 nested; - u16 repeat; - u16 repend; + u32 repeat; + u32 repend; u32 ramcfg; }; +#define nvbios_init(s,o,ARGS...) ({ \ + struct nvbios_init init = { \ + .subdev = (s), \ + .offset = (o), \ + .or = -1, \ + .link = 0, \ + .head = -1, \ + .execute = 1, \ + }; \ + ARGS \ + nvbios_exec(&init); \ +}) int nvbios_exec(struct nvbios_init *); -int nvbios_init(struct nvkm_subdev *, bool execute); + +int nvbios_post(struct nvkm_subdev *, bool execute); #endif diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h index 6a567fe347b3..ff0709652f80 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h @@ -4,6 +4,7 @@ struct nvkm_alarm { struct list_head head; + struct list_head exec; u64 timestamp; void (*func)(struct nvkm_alarm *); }; @@ -25,7 +26,6 @@ struct nvkm_timer { u64 nvkm_timer_read(struct nvkm_timer *); void nvkm_timer_alarm(struct nvkm_timer *, u32 nsec, struct nvkm_alarm *); -void nvkm_timer_alarm_cancel(struct nvkm_timer *, struct nvkm_alarm *); /* Delay based on GPU time (ie. PTIMER). * diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 9a0772ad495a..b998c33af18a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -1533,7 +1533,8 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb, if (conf & 0x100000) entry->i2c_upper_default = true; - entry->hasht = (entry->location << 4) | entry->type; + entry->hasht = (entry->extdev << 8) | (entry->location << 4) | + entry->type; entry->hashm = (entry->heads << 8) | (link << 6) | entry->or; return true; } diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index f802bcd94457..147b22163f9f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -1045,6 +1045,9 @@ nouveau_connector_mode_valid(struct drm_connector *connector, return MODE_BAD; } + if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) + clock *= 2; + if (clock < min_clock) return MODE_CLOCK_LOW; @@ -1321,6 +1324,13 @@ nouveau_connector_create(struct drm_device *dev, int index) break; } + /* HDMI 3D support */ + if ((disp->disp.oclass >= G82_DISP) + && ((type == DRM_MODE_CONNECTOR_DisplayPort) + || (type == DRM_MODE_CONNECTOR_eDP) + || (type == DRM_MODE_CONNECTOR_HDMIA))) + connector->stereo_allowed = true; + /* defaults, will get overridden in detect() */ connector->interlace_allowed = false; connector->doublescan_allowed = false; diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 0f3af939f5aa..b9a109be989c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -335,6 +335,8 @@ nouveau_display_hpd_work(struct work_struct *work) pm_runtime_get_sync(drm->dev->dev); drm_helper_hpd_irq_event(drm->dev); + /* enable polling for external displays */ + drm_kms_helper_poll_enable(drm->dev); pm_runtime_mark_last_busy(drm->dev->dev); pm_runtime_put_sync(drm->dev->dev); @@ -388,10 +390,6 @@ nouveau_display_init(struct drm_device *dev) if (ret) return ret; - /* enable polling for external displays */ - if (!dev->mode_config.poll_enabled) - drm_kms_helper_poll_enable(dev); - /* enable hotplug interrupts */ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { struct nouveau_connector *conn = nouveau_connector(connector); diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index efa5489c5aed..2600b3b9f2e7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -80,7 +80,7 @@ int nouveau_modeset = -1; module_param_named(modeset, nouveau_modeset, int, 0400); MODULE_PARM_DESC(runpm, "disable (0), force enable (1), optimus only default (-1)"); -int nouveau_runtime_pm = -1; +static int nouveau_runtime_pm = -1; module_param_named(runpm, nouveau_runtime_pm, int, 0400); static struct drm_driver driver_stub; @@ -495,13 +495,16 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags) nouveau_fbcon_init(dev); nouveau_led_init(dev); - if (nouveau_runtime_pm != 0) { + if (nouveau_pmops_runtime()) { pm_runtime_use_autosuspend(dev->dev); pm_runtime_set_autosuspend_delay(dev->dev, 5000); pm_runtime_set_active(dev->dev); pm_runtime_allow(dev->dev); pm_runtime_mark_last_busy(dev->dev); pm_runtime_put(dev->dev); + } else { + /* enable polling for external displays */ + drm_kms_helper_poll_enable(dev); } return 0; @@ -524,7 +527,7 @@ nouveau_drm_unload(struct drm_device *dev) { struct nouveau_drm *drm = nouveau_drm(dev); - if (nouveau_runtime_pm != 0) { + if (nouveau_pmops_runtime()) { pm_runtime_get_sync(dev->dev); pm_runtime_forbid(dev->dev); } @@ -723,6 +726,14 @@ nouveau_pmops_thaw(struct device *dev) return nouveau_do_resume(drm_dev, false); } +bool +nouveau_pmops_runtime(void) +{ + if (nouveau_runtime_pm == -1) + return nouveau_is_optimus() || nouveau_is_v1_dsm(); + return nouveau_runtime_pm == 1; +} + static int nouveau_pmops_runtime_suspend(struct device *dev) { @@ -730,14 +741,7 @@ nouveau_pmops_runtime_suspend(struct device *dev) struct drm_device *drm_dev = pci_get_drvdata(pdev); int ret; - if (nouveau_runtime_pm == 0) { - pm_runtime_forbid(dev); - return -EBUSY; - } - - /* are we optimus enabled? */ - if (nouveau_runtime_pm == -1 && !nouveau_is_optimus() && !nouveau_is_v1_dsm()) { - DRM_DEBUG_DRIVER("failing to power off - not optimus\n"); + if (!nouveau_pmops_runtime()) { pm_runtime_forbid(dev); return -EBUSY; } @@ -762,8 +766,10 @@ nouveau_pmops_runtime_resume(struct device *dev) struct nvif_device *device = &nouveau_drm(drm_dev)->client.device; int ret; - if (nouveau_runtime_pm == 0) - return -EINVAL; + if (!nouveau_pmops_runtime()) { + pm_runtime_forbid(dev); + return -EBUSY; + } pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); @@ -774,9 +780,6 @@ nouveau_pmops_runtime_resume(struct device *dev) ret = nouveau_do_resume(drm_dev, true); - if (!drm_dev->mode_config.poll_enabled) - drm_kms_helper_poll_enable(drm_dev); - /* do magic */ nvif_mask(&device->object, 0x088488, (1 << 25), (1 << 25)); vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON); @@ -796,14 +799,7 @@ nouveau_pmops_runtime_idle(struct device *dev) struct nouveau_drm *drm = nouveau_drm(drm_dev); struct drm_crtc *crtc; - if (nouveau_runtime_pm == 0) { - pm_runtime_forbid(dev); - return -EBUSY; - } - - /* are we optimus enabled? */ - if (nouveau_runtime_pm == -1 && !nouveau_is_optimus() && !nouveau_is_v1_dsm()) { - DRM_DEBUG_DRIVER("failing to power off - not optimus\n"); + if (!nouveau_pmops_runtime()) { pm_runtime_forbid(dev); return -EBUSY; } diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index aaa25641fed6..822fe1d4d35e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -108,8 +108,6 @@ nouveau_cli(struct drm_file *fpriv) #include <nvif/object.h> #include <nvif/device.h> -extern int nouveau_runtime_pm; - struct nouveau_drm { struct nouveau_cli client; struct drm_device *dev; @@ -195,6 +193,7 @@ nouveau_drm(struct drm_device *dev) int nouveau_pmops_suspend(struct device *); int nouveau_pmops_resume(struct device *); +bool nouveau_pmops_runtime(void); #include <nvkm/core/tegra.h> diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index 198e5f27682f..e28d966946a1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -42,6 +42,7 @@ struct nouveau_encoder { struct dcb_output *dcb; int or; + int link; struct i2c_adapter *i2c; struct nvkm_i2c_aux *aux; diff --git a/drivers/gpu/drm/nouveau/nouveau_hwmon.c b/drivers/gpu/drm/nouveau/nouveau_hwmon.c index 23b1670c1c2f..7c965648df80 100644 --- a/drivers/gpu/drm/nouveau/nouveau_hwmon.c +++ b/drivers/gpu/drm/nouveau/nouveau_hwmon.c @@ -38,21 +38,6 @@ #include <nvkm/subdev/volt.h> #if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) -static ssize_t -nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf) -{ - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->client.device); - int temp = nvkm_therm_temp_get(therm); - - if (temp < 0) - return temp; - - return snprintf(buf, PAGE_SIZE, "%d\n", temp * 1000); -} -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp, - NULL, 0); static ssize_t nouveau_hwmon_show_temp1_auto_point1_pwm(struct device *d, @@ -60,7 +45,7 @@ nouveau_hwmon_show_temp1_auto_point1_pwm(struct device *d, { return snprintf(buf, PAGE_SIZE, "%d\n", 100); } -static SENSOR_DEVICE_ATTR(temp1_auto_point1_pwm, S_IRUGO, +static SENSOR_DEVICE_ATTR(temp1_auto_point1_pwm, 0444, nouveau_hwmon_show_temp1_auto_point1_pwm, NULL, 0); static ssize_t @@ -92,7 +77,7 @@ nouveau_hwmon_set_temp1_auto_point1_temp(struct device *d, return count; } -static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp, S_IRUGO | S_IWUSR, +static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp, 0644, nouveau_hwmon_temp1_auto_point1_temp, nouveau_hwmon_set_temp1_auto_point1_temp, 0); @@ -125,572 +110,595 @@ nouveau_hwmon_set_temp1_auto_point1_temp_hyst(struct device *d, return count; } -static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp_hyst, S_IRUGO | S_IWUSR, +static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp_hyst, 0644, nouveau_hwmon_temp1_auto_point1_temp_hyst, nouveau_hwmon_set_temp1_auto_point1_temp_hyst, 0); static ssize_t -nouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf) -{ - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->client.device); - - return snprintf(buf, PAGE_SIZE, "%d\n", - therm->attr_get(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK) * 1000); -} -static ssize_t -nouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a, - const char *buf, size_t count) +nouveau_hwmon_get_pwm1_max(struct device *d, + struct device_attribute *a, char *buf) { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); struct nvkm_therm *therm = nvxx_therm(&drm->client.device); - long value; - - if (kstrtol(buf, 10, &value) == -EINVAL) - return count; + int ret; - therm->attr_set(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK, value / 1000); + ret = therm->attr_get(therm, NVKM_THERM_ATTR_FAN_MAX_DUTY); + if (ret < 0) + return ret; - return count; + return sprintf(buf, "%i\n", ret); } -static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp, - nouveau_hwmon_set_max_temp, - 0); static ssize_t -nouveau_hwmon_max_temp_hyst(struct device *d, struct device_attribute *a, - char *buf) +nouveau_hwmon_get_pwm1_min(struct device *d, + struct device_attribute *a, char *buf) { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); struct nvkm_therm *therm = nvxx_therm(&drm->client.device); + int ret; - return snprintf(buf, PAGE_SIZE, "%d\n", - therm->attr_get(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST) * 1000); + ret = therm->attr_get(therm, NVKM_THERM_ATTR_FAN_MIN_DUTY); + if (ret < 0) + return ret; + + return sprintf(buf, "%i\n", ret); } + static ssize_t -nouveau_hwmon_set_max_temp_hyst(struct device *d, struct device_attribute *a, - const char *buf, size_t count) +nouveau_hwmon_set_pwm1_min(struct device *d, struct device_attribute *a, + const char *buf, size_t count) { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); struct nvkm_therm *therm = nvxx_therm(&drm->client.device); long value; + int ret; if (kstrtol(buf, 10, &value) == -EINVAL) - return count; + return -EINVAL; - therm->attr_set(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST, - value / 1000); + ret = therm->attr_set(therm, NVKM_THERM_ATTR_FAN_MIN_DUTY, value); + if (ret < 0) + return ret; return count; } -static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, - nouveau_hwmon_max_temp_hyst, - nouveau_hwmon_set_max_temp_hyst, 0); - -static ssize_t -nouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a, - char *buf) -{ - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->client.device); +static SENSOR_DEVICE_ATTR(pwm1_min, 0644, + nouveau_hwmon_get_pwm1_min, + nouveau_hwmon_set_pwm1_min, 0); - return snprintf(buf, PAGE_SIZE, "%d\n", - therm->attr_get(therm, NVKM_THERM_ATTR_THRS_CRITICAL) * 1000); -} static ssize_t -nouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a, - const char *buf, - size_t count) +nouveau_hwmon_set_pwm1_max(struct device *d, struct device_attribute *a, + const char *buf, size_t count) { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); struct nvkm_therm *therm = nvxx_therm(&drm->client.device); long value; + int ret; if (kstrtol(buf, 10, &value) == -EINVAL) - return count; + return -EINVAL; - therm->attr_set(therm, NVKM_THERM_ATTR_THRS_CRITICAL, value / 1000); + ret = therm->attr_set(therm, NVKM_THERM_ATTR_FAN_MAX_DUTY, value); + if (ret < 0) + return ret; return count; } -static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR, - nouveau_hwmon_critical_temp, - nouveau_hwmon_set_critical_temp, - 0); +static SENSOR_DEVICE_ATTR(pwm1_max, 0644, + nouveau_hwmon_get_pwm1_max, + nouveau_hwmon_set_pwm1_max, 0); -static ssize_t -nouveau_hwmon_critical_temp_hyst(struct device *d, struct device_attribute *a, - char *buf) -{ - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->client.device); +static struct attribute *pwm_fan_sensor_attrs[] = { + &sensor_dev_attr_pwm1_min.dev_attr.attr, + &sensor_dev_attr_pwm1_max.dev_attr.attr, + NULL +}; +static const struct attribute_group pwm_fan_sensor_group = { + .attrs = pwm_fan_sensor_attrs, +}; - return snprintf(buf, PAGE_SIZE, "%d\n", - therm->attr_get(therm, NVKM_THERM_ATTR_THRS_CRITICAL_HYST) * 1000); -} -static ssize_t -nouveau_hwmon_set_critical_temp_hyst(struct device *d, - struct device_attribute *a, - const char *buf, - size_t count) -{ - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->client.device); - long value; +static struct attribute *temp1_auto_point_sensor_attrs[] = { + &sensor_dev_attr_temp1_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_temp1_auto_point1_temp_hyst.dev_attr.attr, + NULL +}; +static const struct attribute_group temp1_auto_point_sensor_group = { + .attrs = temp1_auto_point_sensor_attrs, +}; - if (kstrtol(buf, 10, &value) == -EINVAL) - return count; +#define N_ATTR_GROUPS 3 - therm->attr_set(therm, NVKM_THERM_ATTR_THRS_CRITICAL_HYST, - value / 1000); +static const u32 nouveau_config_chip[] = { + HWMON_C_UPDATE_INTERVAL, + 0 +}; - return count; -} -static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO | S_IWUSR, - nouveau_hwmon_critical_temp_hyst, - nouveau_hwmon_set_critical_temp_hyst, 0); -static ssize_t -nouveau_hwmon_emergency_temp(struct device *d, struct device_attribute *a, - char *buf) -{ - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->client.device); +static const u32 nouveau_config_in[] = { + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_LABEL, + 0 +}; - return snprintf(buf, PAGE_SIZE, "%d\n", - therm->attr_get(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN) * 1000); -} -static ssize_t -nouveau_hwmon_set_emergency_temp(struct device *d, struct device_attribute *a, - const char *buf, - size_t count) -{ - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->client.device); - long value; +static const u32 nouveau_config_temp[] = { + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_EMERGENCY | + HWMON_T_EMERGENCY_HYST, + 0 +}; - if (kstrtol(buf, 10, &value) == -EINVAL) - return count; +static const u32 nouveau_config_fan[] = { + HWMON_F_INPUT, + 0 +}; - therm->attr_set(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN, value / 1000); +static const u32 nouveau_config_pwm[] = { + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, + 0 +}; - return count; -} -static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO | S_IWUSR, - nouveau_hwmon_emergency_temp, - nouveau_hwmon_set_emergency_temp, - 0); +static const u32 nouveau_config_power[] = { + HWMON_P_INPUT | HWMON_P_CAP_MAX | HWMON_P_CRIT, + 0 +}; -static ssize_t -nouveau_hwmon_emergency_temp_hyst(struct device *d, struct device_attribute *a, - char *buf) -{ - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->client.device); +static const struct hwmon_channel_info nouveau_chip = { + .type = hwmon_chip, + .config = nouveau_config_chip, +}; - return snprintf(buf, PAGE_SIZE, "%d\n", - therm->attr_get(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST) * 1000); -} -static ssize_t -nouveau_hwmon_set_emergency_temp_hyst(struct device *d, - struct device_attribute *a, - const char *buf, - size_t count) -{ - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->client.device); - long value; +static const struct hwmon_channel_info nouveau_temp = { + .type = hwmon_temp, + .config = nouveau_config_temp, +}; - if (kstrtol(buf, 10, &value) == -EINVAL) - return count; +static const struct hwmon_channel_info nouveau_fan = { + .type = hwmon_fan, + .config = nouveau_config_fan, +}; - therm->attr_set(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST, - value / 1000); +static const struct hwmon_channel_info nouveau_in = { + .type = hwmon_in, + .config = nouveau_config_in, +}; - return count; -} -static SENSOR_DEVICE_ATTR(temp1_emergency_hyst, S_IRUGO | S_IWUSR, - nouveau_hwmon_emergency_temp_hyst, - nouveau_hwmon_set_emergency_temp_hyst, - 0); - -static ssize_t nouveau_hwmon_show_name(struct device *dev, - struct device_attribute *attr, - char *buf) +static const struct hwmon_channel_info nouveau_pwm = { + .type = hwmon_pwm, + .config = nouveau_config_pwm, +}; + +static const struct hwmon_channel_info nouveau_power = { + .type = hwmon_power, + .config = nouveau_config_power, +}; + +static const struct hwmon_channel_info *nouveau_info[] = { + &nouveau_chip, + &nouveau_temp, + &nouveau_fan, + &nouveau_in, + &nouveau_pwm, + &nouveau_power, + NULL +}; + +static umode_t +nouveau_chip_is_visible(const void *data, u32 attr, int channel) { - return sprintf(buf, "nouveau\n"); + switch (attr) { + case hwmon_chip_update_interval: + return 0444; + default: + return 0; + } } -static SENSOR_DEVICE_ATTR(name, S_IRUGO, nouveau_hwmon_show_name, NULL, 0); -static ssize_t nouveau_hwmon_show_update_rate(struct device *dev, - struct device_attribute *attr, - char *buf) +static umode_t +nouveau_power_is_visible(const void *data, u32 attr, int channel) { - return sprintf(buf, "1000\n"); + struct nouveau_drm *drm = nouveau_drm((struct drm_device *)data); + struct nvkm_iccsense *iccsense = nvxx_iccsense(&drm->client.device); + + if (!iccsense || !iccsense->data_valid || list_empty(&iccsense->rails)) + return 0; + + switch (attr) { + case hwmon_power_input: + return 0444; + case hwmon_power_max: + if (iccsense->power_w_max) + return 0444; + return 0; + case hwmon_power_crit: + if (iccsense->power_w_crit) + return 0444; + return 0; + default: + return 0; + } } -static SENSOR_DEVICE_ATTR(update_rate, S_IRUGO, - nouveau_hwmon_show_update_rate, - NULL, 0); -static ssize_t -nouveau_hwmon_show_fan1_input(struct device *d, struct device_attribute *attr, - char *buf) +static umode_t +nouveau_temp_is_visible(const void *data, u32 attr, int channel) { - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_drm *drm = nouveau_drm((struct drm_device *)data); struct nvkm_therm *therm = nvxx_therm(&drm->client.device); - return snprintf(buf, PAGE_SIZE, "%d\n", nvkm_therm_fan_sense(therm)); + if (therm && therm->attr_get && nvkm_therm_temp_get(therm) < 0) + return 0; + + switch (attr) { + case hwmon_temp_input: + return 0444; + case hwmon_temp_max: + case hwmon_temp_max_hyst: + case hwmon_temp_crit: + case hwmon_temp_crit_hyst: + case hwmon_temp_emergency: + case hwmon_temp_emergency_hyst: + return 0644; + default: + return 0; + } } -static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, nouveau_hwmon_show_fan1_input, - NULL, 0); - static ssize_t -nouveau_hwmon_get_pwm1_enable(struct device *d, - struct device_attribute *a, char *buf) +static umode_t +nouveau_pwm_is_visible(const void *data, u32 attr, int channel) { - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_drm *drm = nouveau_drm((struct drm_device *)data); struct nvkm_therm *therm = nvxx_therm(&drm->client.device); - int ret; - ret = therm->attr_get(therm, NVKM_THERM_ATTR_FAN_MODE); - if (ret < 0) - return ret; + if (therm && therm->attr_get && therm->fan_get && + therm->fan_get(therm) < 0) + return 0; - return sprintf(buf, "%i\n", ret); + switch (attr) { + case hwmon_pwm_enable: + case hwmon_pwm_input: + return 0644; + default: + return 0; + } } -static ssize_t -nouveau_hwmon_set_pwm1_enable(struct device *d, struct device_attribute *a, - const char *buf, size_t count) +static umode_t +nouveau_input_is_visible(const void *data, u32 attr, int channel) { - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->client.device); - long value; - int ret; - - ret = kstrtol(buf, 10, &value); - if (ret) - return ret; + struct nouveau_drm *drm = nouveau_drm((struct drm_device *)data); + struct nvkm_volt *volt = nvxx_volt(&drm->client.device); - ret = therm->attr_set(therm, NVKM_THERM_ATTR_FAN_MODE, value); - if (ret) - return ret; - else - return count; + if (!volt || nvkm_volt_get(volt) < 0) + return 0; + + switch (attr) { + case hwmon_in_input: + case hwmon_in_label: + case hwmon_in_min: + case hwmon_in_max: + return 0444; + default: + return 0; + } } -static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, - nouveau_hwmon_get_pwm1_enable, - nouveau_hwmon_set_pwm1_enable, 0); -static ssize_t -nouveau_hwmon_get_pwm1(struct device *d, struct device_attribute *a, char *buf) +static umode_t +nouveau_fan_is_visible(const void *data, u32 attr, int channel) { - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_drm *drm = nouveau_drm((struct drm_device *)data); struct nvkm_therm *therm = nvxx_therm(&drm->client.device); - int ret; - ret = therm->fan_get(therm); - if (ret < 0) - return ret; + if (!therm || !therm->attr_get || nvkm_therm_fan_sense(therm) < 0) + return 0; - return sprintf(buf, "%i\n", ret); + switch (attr) { + case hwmon_fan_input: + return 0444; + default: + return 0; + } } -static ssize_t -nouveau_hwmon_set_pwm1(struct device *d, struct device_attribute *a, - const char *buf, size_t count) +static int +nouveau_chip_read(struct device *dev, u32 attr, int channel, long *val) { - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->client.device); - int ret = -ENODEV; - long value; - - if (kstrtol(buf, 10, &value) == -EINVAL) - return -EINVAL; - - ret = therm->fan_set(therm, value); - if (ret) - return ret; + switch (attr) { + case hwmon_chip_update_interval: + *val = 1000; + break; + default: + return -EOPNOTSUPP; + } - return count; + return 0; } -static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, - nouveau_hwmon_get_pwm1, - nouveau_hwmon_set_pwm1, 0); - -static ssize_t -nouveau_hwmon_get_pwm1_min(struct device *d, - struct device_attribute *a, char *buf) +static int +nouveau_temp_read(struct device *dev, u32 attr, int channel, long *val) { - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); + struct drm_device *drm_dev = dev_get_drvdata(dev); + struct nouveau_drm *drm = nouveau_drm(drm_dev); struct nvkm_therm *therm = nvxx_therm(&drm->client.device); int ret; - ret = therm->attr_get(therm, NVKM_THERM_ATTR_FAN_MIN_DUTY); - if (ret < 0) - return ret; + if (!therm || !therm->attr_get) + return -EOPNOTSUPP; + + switch (attr) { + case hwmon_temp_input: + ret = nvkm_therm_temp_get(therm); + *val = ret < 0 ? ret : (ret * 1000); + break; + case hwmon_temp_max: + *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK) + * 1000; + break; + case hwmon_temp_max_hyst: + *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST) + * 1000; + break; + case hwmon_temp_crit: + *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_CRITICAL) + * 1000; + break; + case hwmon_temp_crit_hyst: + *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_CRITICAL_HYST) + * 1000; + break; + case hwmon_temp_emergency: + *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN) + * 1000; + break; + case hwmon_temp_emergency_hyst: + *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST) + * 1000; + break; + default: + return -EOPNOTSUPP; + } - return sprintf(buf, "%i\n", ret); + return 0; } -static ssize_t -nouveau_hwmon_set_pwm1_min(struct device *d, struct device_attribute *a, - const char *buf, size_t count) +static int +nouveau_fan_read(struct device *dev, u32 attr, int channel, long *val) { - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); + struct drm_device *drm_dev = dev_get_drvdata(dev); + struct nouveau_drm *drm = nouveau_drm(drm_dev); struct nvkm_therm *therm = nvxx_therm(&drm->client.device); - long value; - int ret; - if (kstrtol(buf, 10, &value) == -EINVAL) - return -EINVAL; + if (!therm) + return -EOPNOTSUPP; - ret = therm->attr_set(therm, NVKM_THERM_ATTR_FAN_MIN_DUTY, value); - if (ret < 0) - return ret; + switch (attr) { + case hwmon_fan_input: + *val = nvkm_therm_fan_sense(therm); + break; + default: + return -EOPNOTSUPP; + } - return count; + return 0; } -static SENSOR_DEVICE_ATTR(pwm1_min, S_IRUGO | S_IWUSR, - nouveau_hwmon_get_pwm1_min, - nouveau_hwmon_set_pwm1_min, 0); - -static ssize_t -nouveau_hwmon_get_pwm1_max(struct device *d, - struct device_attribute *a, char *buf) +static int +nouveau_in_read(struct device *dev, u32 attr, int channel, long *val) { - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->client.device); + struct drm_device *drm_dev = dev_get_drvdata(dev); + struct nouveau_drm *drm = nouveau_drm(drm_dev); + struct nvkm_volt *volt = nvxx_volt(&drm->client.device); int ret; - ret = therm->attr_get(therm, NVKM_THERM_ATTR_FAN_MAX_DUTY); - if (ret < 0) - return ret; + if (!volt) + return -EOPNOTSUPP; + + switch (attr) { + case hwmon_in_input: + ret = nvkm_volt_get(volt); + *val = ret < 0 ? ret : (ret / 1000); + break; + case hwmon_in_min: + *val = volt->min_uv > 0 ? (volt->min_uv / 1000) : -ENODEV; + break; + case hwmon_in_max: + *val = volt->max_uv > 0 ? (volt->max_uv / 1000) : -ENODEV; + break; + default: + return -EOPNOTSUPP; + } - return sprintf(buf, "%i\n", ret); + return 0; } -static ssize_t -nouveau_hwmon_set_pwm1_max(struct device *d, struct device_attribute *a, - const char *buf, size_t count) +static int +nouveau_pwm_read(struct device *dev, u32 attr, int channel, long *val) { - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); + struct drm_device *drm_dev = dev_get_drvdata(dev); + struct nouveau_drm *drm = nouveau_drm(drm_dev); struct nvkm_therm *therm = nvxx_therm(&drm->client.device); - long value; - int ret; - if (kstrtol(buf, 10, &value) == -EINVAL) - return -EINVAL; - - ret = therm->attr_set(therm, NVKM_THERM_ATTR_FAN_MAX_DUTY, value); - if (ret < 0) - return ret; + if (!therm || !therm->attr_get || !therm->fan_get) + return -EOPNOTSUPP; + + switch (attr) { + case hwmon_pwm_enable: + *val = therm->attr_get(therm, NVKM_THERM_ATTR_FAN_MODE); + break; + case hwmon_pwm_input: + *val = therm->fan_get(therm); + break; + default: + return -EOPNOTSUPP; + } - return count; + return 0; } -static SENSOR_DEVICE_ATTR(pwm1_max, S_IRUGO | S_IWUSR, - nouveau_hwmon_get_pwm1_max, - nouveau_hwmon_set_pwm1_max, 0); - -static ssize_t -nouveau_hwmon_get_in0_input(struct device *d, - struct device_attribute *a, char *buf) +static int +nouveau_power_read(struct device *dev, u32 attr, int channel, long *val) { - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_volt *volt = nvxx_volt(&drm->client.device); - int ret; + struct drm_device *drm_dev = dev_get_drvdata(dev); + struct nouveau_drm *drm = nouveau_drm(drm_dev); + struct nvkm_iccsense *iccsense = nvxx_iccsense(&drm->client.device); - ret = nvkm_volt_get(volt); - if (ret < 0) - return ret; + if (!iccsense) + return -EOPNOTSUPP; + + switch (attr) { + case hwmon_power_input: + *val = nvkm_iccsense_read_all(iccsense); + break; + case hwmon_power_max: + *val = iccsense->power_w_max; + break; + case hwmon_power_crit: + *val = iccsense->power_w_crit; + break; + default: + return -EOPNOTSUPP; + } - return sprintf(buf, "%i\n", ret / 1000); + return 0; } -static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, - nouveau_hwmon_get_in0_input, NULL, 0); - -static ssize_t -nouveau_hwmon_get_in0_min(struct device *d, - struct device_attribute *a, char *buf) +static int +nouveau_temp_write(struct device *dev, u32 attr, int channel, long val) { - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_volt *volt = nvxx_volt(&drm->client.device); - - if (!volt || !volt->min_uv) - return -ENODEV; + struct drm_device *drm_dev = dev_get_drvdata(dev); + struct nouveau_drm *drm = nouveau_drm(drm_dev); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); - return sprintf(buf, "%i\n", volt->min_uv / 1000); + if (!therm || !therm->attr_set) + return -EOPNOTSUPP; + + switch (attr) { + case hwmon_temp_max: + return therm->attr_set(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK, + val / 1000); + case hwmon_temp_max_hyst: + return therm->attr_set(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST, + val / 1000); + case hwmon_temp_crit: + return therm->attr_set(therm, NVKM_THERM_ATTR_THRS_CRITICAL, + val / 1000); + case hwmon_temp_crit_hyst: + return therm->attr_set(therm, NVKM_THERM_ATTR_THRS_CRITICAL_HYST, + val / 1000); + case hwmon_temp_emergency: + return therm->attr_set(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN, + val / 1000); + case hwmon_temp_emergency_hyst: + return therm->attr_set(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST, + val / 1000); + default: + return -EOPNOTSUPP; + } } -static SENSOR_DEVICE_ATTR(in0_min, S_IRUGO, - nouveau_hwmon_get_in0_min, NULL, 0); - -static ssize_t -nouveau_hwmon_get_in0_max(struct device *d, - struct device_attribute *a, char *buf) +static int +nouveau_pwm_write(struct device *dev, u32 attr, int channel, long val) { - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_volt *volt = nvxx_volt(&drm->client.device); + struct drm_device *drm_dev = dev_get_drvdata(dev); + struct nouveau_drm *drm = nouveau_drm(drm_dev); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); - if (!volt || !volt->max_uv) - return -ENODEV; + if (!therm || !therm->attr_set) + return -EOPNOTSUPP; - return sprintf(buf, "%i\n", volt->max_uv / 1000); + switch (attr) { + case hwmon_pwm_input: + return therm->fan_set(therm, val); + case hwmon_pwm_enable: + return therm->attr_set(therm, NVKM_THERM_ATTR_FAN_MODE, val); + default: + return -EOPNOTSUPP; + } } -static SENSOR_DEVICE_ATTR(in0_max, S_IRUGO, - nouveau_hwmon_get_in0_max, NULL, 0); - -static ssize_t -nouveau_hwmon_get_in0_label(struct device *d, - struct device_attribute *a, char *buf) -{ - return sprintf(buf, "GPU core\n"); +static umode_t +nouveau_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, + int channel) +{ + switch (type) { + case hwmon_chip: + return nouveau_chip_is_visible(data, attr, channel); + case hwmon_temp: + return nouveau_temp_is_visible(data, attr, channel); + case hwmon_fan: + return nouveau_fan_is_visible(data, attr, channel); + case hwmon_in: + return nouveau_input_is_visible(data, attr, channel); + case hwmon_pwm: + return nouveau_pwm_is_visible(data, attr, channel); + case hwmon_power: + return nouveau_power_is_visible(data, attr, channel); + default: + return 0; + } } -static SENSOR_DEVICE_ATTR(in0_label, S_IRUGO, - nouveau_hwmon_get_in0_label, NULL, 0); +static const char input_label[] = "GPU core"; -static ssize_t -nouveau_hwmon_get_power1_input(struct device *d, struct device_attribute *a, - char *buf) +static int +nouveau_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, const char **buf) { - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_iccsense *iccsense = nvxx_iccsense(&drm->client.device); - int result = nvkm_iccsense_read_all(iccsense); - - if (result < 0) - return result; - - return sprintf(buf, "%i\n", result); -} - -static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, - nouveau_hwmon_get_power1_input, NULL, 0); + if (type == hwmon_in && attr == hwmon_in_label) { + *buf = input_label; + return 0; + } -static ssize_t -nouveau_hwmon_get_power1_max(struct device *d, struct device_attribute *a, - char *buf) -{ - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_iccsense *iccsense = nvxx_iccsense(&drm->client.device); - return sprintf(buf, "%i\n", iccsense->power_w_max); + return -EOPNOTSUPP; +} + +static int +nouveau_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long *val) +{ + switch (type) { + case hwmon_chip: + return nouveau_chip_read(dev, attr, channel, val); + case hwmon_temp: + return nouveau_temp_read(dev, attr, channel, val); + case hwmon_fan: + return nouveau_fan_read(dev, attr, channel, val); + case hwmon_in: + return nouveau_in_read(dev, attr, channel, val); + case hwmon_pwm: + return nouveau_pwm_read(dev, attr, channel, val); + case hwmon_power: + return nouveau_power_read(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } } -static SENSOR_DEVICE_ATTR(power1_max, S_IRUGO, - nouveau_hwmon_get_power1_max, NULL, 0); - -static ssize_t -nouveau_hwmon_get_power1_crit(struct device *d, struct device_attribute *a, - char *buf) +static int +nouveau_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long val) { - struct drm_device *dev = dev_get_drvdata(d); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_iccsense *iccsense = nvxx_iccsense(&drm->client.device); - return sprintf(buf, "%i\n", iccsense->power_w_crit); + switch (type) { + case hwmon_temp: + return nouveau_temp_write(dev, attr, channel, val); + case hwmon_pwm: + return nouveau_pwm_write(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } } -static SENSOR_DEVICE_ATTR(power1_crit, S_IRUGO, - nouveau_hwmon_get_power1_crit, NULL, 0); - -static struct attribute *hwmon_default_attributes[] = { - &sensor_dev_attr_name.dev_attr.attr, - &sensor_dev_attr_update_rate.dev_attr.attr, - NULL -}; -static struct attribute *hwmon_temp_attributes[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp1_auto_point1_pwm.dev_attr.attr, - &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr, - &sensor_dev_attr_temp1_auto_point1_temp_hyst.dev_attr.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp1_crit.dev_attr.attr, - &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, - &sensor_dev_attr_temp1_emergency.dev_attr.attr, - &sensor_dev_attr_temp1_emergency_hyst.dev_attr.attr, - NULL -}; -static struct attribute *hwmon_fan_rpm_attributes[] = { - &sensor_dev_attr_fan1_input.dev_attr.attr, - NULL -}; -static struct attribute *hwmon_pwm_fan_attributes[] = { - &sensor_dev_attr_pwm1_enable.dev_attr.attr, - &sensor_dev_attr_pwm1.dev_attr.attr, - &sensor_dev_attr_pwm1_min.dev_attr.attr, - &sensor_dev_attr_pwm1_max.dev_attr.attr, - NULL -}; - -static struct attribute *hwmon_in0_attributes[] = { - &sensor_dev_attr_in0_input.dev_attr.attr, - &sensor_dev_attr_in0_min.dev_attr.attr, - &sensor_dev_attr_in0_max.dev_attr.attr, - &sensor_dev_attr_in0_label.dev_attr.attr, - NULL +static const struct hwmon_ops nouveau_hwmon_ops = { + .is_visible = nouveau_is_visible, + .read = nouveau_read, + .read_string = nouveau_read_string, + .write = nouveau_write, }; -static struct attribute *hwmon_power_attributes[] = { - &sensor_dev_attr_power1_input.dev_attr.attr, - NULL -}; - -static struct attribute *hwmon_power_caps_attributes[] = { - &sensor_dev_attr_power1_max.dev_attr.attr, - &sensor_dev_attr_power1_crit.dev_attr.attr, - NULL -}; - -static const struct attribute_group hwmon_default_attrgroup = { - .attrs = hwmon_default_attributes, -}; -static const struct attribute_group hwmon_temp_attrgroup = { - .attrs = hwmon_temp_attributes, -}; -static const struct attribute_group hwmon_fan_rpm_attrgroup = { - .attrs = hwmon_fan_rpm_attributes, -}; -static const struct attribute_group hwmon_pwm_fan_attrgroup = { - .attrs = hwmon_pwm_fan_attributes, -}; -static const struct attribute_group hwmon_in0_attrgroup = { - .attrs = hwmon_in0_attributes, -}; -static const struct attribute_group hwmon_power_attrgroup = { - .attrs = hwmon_power_attributes, -}; -static const struct attribute_group hwmon_power_caps_attrgroup = { - .attrs = hwmon_power_caps_attributes, +static const struct hwmon_chip_info nouveau_chip_info = { + .ops = &nouveau_hwmon_ops, + .info = nouveau_info, }; #endif @@ -700,90 +708,36 @@ nouveau_hwmon_init(struct drm_device *dev) #if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) struct nouveau_drm *drm = nouveau_drm(dev); struct nvkm_therm *therm = nvxx_therm(&drm->client.device); - struct nvkm_volt *volt = nvxx_volt(&drm->client.device); - struct nvkm_iccsense *iccsense = nvxx_iccsense(&drm->client.device); + const struct attribute_group *special_groups[N_ATTR_GROUPS]; struct nouveau_hwmon *hwmon; struct device *hwmon_dev; int ret = 0; + int i = 0; hwmon = drm->hwmon = kzalloc(sizeof(*hwmon), GFP_KERNEL); if (!hwmon) return -ENOMEM; hwmon->dev = dev; - hwmon_dev = hwmon_device_register(dev->dev); + if (therm && therm->attr_get && therm->attr_set) { + if (nvkm_therm_temp_get(therm) >= 0) + special_groups[i++] = &temp1_auto_point_sensor_group; + if (therm->fan_get && therm->fan_get(therm) >= 0) + special_groups[i++] = &pwm_fan_sensor_group; + } + + special_groups[i] = 0; + hwmon_dev = hwmon_device_register_with_info(dev->dev, "nouveau", dev, + &nouveau_chip_info, + special_groups); if (IS_ERR(hwmon_dev)) { ret = PTR_ERR(hwmon_dev); NV_ERROR(drm, "Unable to register hwmon device: %d\n", ret); return ret; } - dev_set_drvdata(hwmon_dev, dev); - - /* set the default attributes */ - ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_default_attrgroup); - if (ret) - goto error; - - if (therm && therm->attr_get && therm->attr_set) { - /* if the card has a working thermal sensor */ - if (nvkm_therm_temp_get(therm) >= 0) { - ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_temp_attrgroup); - if (ret) - goto error; - } - - /* if the card has a pwm fan */ - /*XXX: incorrect, need better detection for this, some boards have - * the gpio entries for pwm fan control even when there's no - * actual fan connected to it... therm table? */ - if (therm->fan_get && therm->fan_get(therm) >= 0) { - ret = sysfs_create_group(&hwmon_dev->kobj, - &hwmon_pwm_fan_attrgroup); - if (ret) - goto error; - } - } - - /* if the card can read the fan rpm */ - if (therm && nvkm_therm_fan_sense(therm) >= 0) { - ret = sysfs_create_group(&hwmon_dev->kobj, - &hwmon_fan_rpm_attrgroup); - if (ret) - goto error; - } - - if (volt && nvkm_volt_get(volt) >= 0) { - ret = sysfs_create_group(&hwmon_dev->kobj, - &hwmon_in0_attrgroup); - - if (ret) - goto error; - } - - if (iccsense && iccsense->data_valid && !list_empty(&iccsense->rails)) { - ret = sysfs_create_group(&hwmon_dev->kobj, - &hwmon_power_attrgroup); - - if (ret) - goto error; - - if (iccsense->power_w_max && iccsense->power_w_crit) { - ret = sysfs_create_group(&hwmon_dev->kobj, - &hwmon_power_caps_attrgroup); - if (ret) - goto error; - } - } hwmon->hwmon = hwmon_dev; - return 0; - -error: - NV_ERROR(drm, "Unable to create some hwmon sysfs files: %d\n", ret); - hwmon_device_unregister(hwmon_dev); - hwmon->hwmon = NULL; - return ret; #else return 0; #endif @@ -795,17 +749,8 @@ nouveau_hwmon_fini(struct drm_device *dev) #if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) struct nouveau_hwmon *hwmon = nouveau_hwmon(dev); - if (hwmon->hwmon) { - sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_default_attrgroup); - sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_temp_attrgroup); - sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_pwm_fan_attrgroup); - sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_fan_rpm_attrgroup); - sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_in0_attrgroup); - sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_power_attrgroup); - sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_power_caps_attrgroup); - + if (hwmon->hwmon) hwmon_device_unregister(hwmon->hwmon); - } nouveau_drm(dev)->hwmon = NULL; kfree(hwmon); diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c index a4aacbc0cec8..48393a4f6331 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vga.c +++ b/drivers/gpu/drm/nouveau/nouveau_vga.c @@ -87,7 +87,7 @@ void nouveau_vga_init(struct nouveau_drm *drm) { struct drm_device *dev = drm->dev; - bool runtime = false; + bool runtime = nouveau_pmops_runtime(); /* only relevant for PCI devices */ if (!dev->pdev) @@ -99,10 +99,6 @@ nouveau_vga_init(struct nouveau_drm *drm) if (pci_is_thunderbolt_attached(dev->pdev)) return; - if (nouveau_runtime_pm == 1) - runtime = true; - if ((nouveau_runtime_pm == -1) && (nouveau_is_optimus() || nouveau_is_v1_dsm())) - runtime = true; vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops, runtime); if (runtime && nouveau_is_v1_dsm() && !nouveau_is_optimus()) @@ -113,18 +109,17 @@ void nouveau_vga_fini(struct nouveau_drm *drm) { struct drm_device *dev = drm->dev; - bool runtime = false; + bool runtime = nouveau_pmops_runtime(); + + /* only relevant for PCI devices */ + if (!dev->pdev) + return; vga_client_register(dev->pdev, NULL, NULL, NULL); if (pci_is_thunderbolt_attached(dev->pdev)) return; - if (nouveau_runtime_pm == 1) - runtime = true; - if ((nouveau_runtime_pm == -1) && (nouveau_is_optimus() || nouveau_is_v1_dsm())) - runtime = true; - vga_switcheroo_unregister_client(dev->pdev); if (runtime && nouveau_is_v1_dsm() && !nouveau_is_optimus()) vga_switcheroo_fini_domain_pm_ops(drm->dev->dev); diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 28cb24624c31..42a85c14aea0 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -23,6 +23,7 @@ */ #include <linux/dma-mapping.h> +#include <linux/hdmi.h> #include <drm/drmP.h> #include <drm/drm_atomic.h> @@ -31,6 +32,7 @@ #include <drm/drm_dp_helper.h> #include <drm/drm_fb_helper.h> #include <drm/drm_plane_helper.h> +#include <drm/drm_edid.h> #include <nvif/class.h> #include <nvif/cl0002.h> @@ -1965,6 +1967,7 @@ nv50_head_atomic_check_view(struct nv50_head_atom *armh, struct drm_display_mode *umode = &asyh->state.mode; int mode = asyc->scaler.mode; struct edid *edid; + int umode_vdisplay, omode_hdisplay, omode_vdisplay; if (connector->edid_blob_ptr) edid = (struct edid *)connector->edid_blob_ptr->data; @@ -1979,12 +1982,18 @@ nv50_head_atomic_check_view(struct nv50_head_atom *armh, mode = DRM_MODE_SCALE_FULLSCREEN; } + /* For the user-specified mode, we must ignore doublescan and + * the like, but honor frame packing. + */ + umode_vdisplay = umode->vdisplay; + if ((umode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) + umode_vdisplay += umode->vtotal; asyh->view.iW = umode->hdisplay; - asyh->view.iH = umode->vdisplay; - asyh->view.oW = omode->hdisplay; - asyh->view.oH = omode->vdisplay; - if (omode->flags & DRM_MODE_FLAG_DBLSCAN) - asyh->view.oH *= 2; + asyh->view.iH = umode_vdisplay; + /* For the output mode, we can just use the stock helper. */ + drm_mode_get_hv_timing(omode, &omode_hdisplay, &omode_vdisplay); + asyh->view.oW = omode_hdisplay; + asyh->view.oH = omode_vdisplay; /* Add overscan compensation if necessary, will keep the aspect * ratio the same as the backend mode unless overridden by the @@ -2014,7 +2023,7 @@ nv50_head_atomic_check_view(struct nv50_head_atom *armh, switch (mode) { case DRM_MODE_SCALE_CENTER: asyh->view.oW = min((u16)umode->hdisplay, asyh->view.oW); - asyh->view.oH = min((u16)umode->vdisplay, asyh->view.oH); + asyh->view.oH = min((u16)umode_vdisplay, asyh->view.oH); /* fall-through */ case DRM_MODE_SCALE_ASPECT: if (asyh->view.oH < asyh->view.oW) { @@ -2036,34 +2045,37 @@ static void nv50_head_atomic_check_mode(struct nv50_head *head, struct nv50_head_atom *asyh) { struct drm_display_mode *mode = &asyh->state.adjusted_mode; - u32 ilace = (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 1; - u32 vscan = (mode->flags & DRM_MODE_FLAG_DBLSCAN) ? 2 : 1; - u32 hbackp = mode->htotal - mode->hsync_end; - u32 vbackp = (mode->vtotal - mode->vsync_end) * vscan / ilace; - u32 hfrontp = mode->hsync_start - mode->hdisplay; - u32 vfrontp = (mode->vsync_start - mode->vdisplay) * vscan / ilace; - u32 blankus; struct nv50_head_mode *m = &asyh->mode; + u32 blankus; - m->h.active = mode->htotal; - m->h.synce = mode->hsync_end - mode->hsync_start - 1; - m->h.blanke = m->h.synce + hbackp; - m->h.blanks = mode->htotal - hfrontp - 1; + drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE); - m->v.active = mode->vtotal * vscan / ilace; - m->v.synce = ((mode->vsync_end - mode->vsync_start) * vscan / ilace) - 1; - m->v.blanke = m->v.synce + vbackp; - m->v.blanks = m->v.active - vfrontp - 1; + /* + * DRM modes are defined in terms of a repeating interval + * starting with the active display area. The hardware modes + * are defined in terms of a repeating interval starting one + * unit (pixel or line) into the sync pulse. So, add bias. + */ + + m->h.active = mode->crtc_htotal; + m->h.synce = mode->crtc_hsync_end - mode->crtc_hsync_start - 1; + m->h.blanke = mode->crtc_hblank_end - mode->crtc_hsync_start - 1; + m->h.blanks = m->h.blanke + mode->crtc_hdisplay; + + m->v.active = mode->crtc_vtotal; + m->v.synce = mode->crtc_vsync_end - mode->crtc_vsync_start - 1; + m->v.blanke = mode->crtc_vblank_end - mode->crtc_vsync_start - 1; + m->v.blanks = m->v.blanke + mode->crtc_vdisplay; /*XXX: Safe underestimate, even "0" works */ - blankus = (m->v.active - mode->vdisplay - 2) * m->h.active; + blankus = (m->v.active - mode->crtc_vdisplay - 2) * m->h.active; blankus *= 1000; - blankus /= mode->clock; + blankus /= mode->crtc_clock; m->v.blankus = blankus; if (mode->flags & DRM_MODE_FLAG_INTERLACE) { - m->v.blank2e = m->v.active + m->v.synce + vbackp; - m->v.blank2s = m->v.blank2e + (mode->vdisplay * vscan / ilace); + m->v.blank2e = m->v.active + m->v.blanke; + m->v.blank2s = m->v.blank2e + mode->crtc_vdisplay; m->v.active = (m->v.active * 2) + 1; m->interlace = true; } else { @@ -2071,9 +2083,8 @@ nv50_head_atomic_check_mode(struct nv50_head *head, struct nv50_head_atom *asyh) m->v.blank2s = 1; m->interlace = false; } - m->clock = mode->clock; + m->clock = mode->crtc_clock; - drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); asyh->set.mode = true; } @@ -2107,7 +2118,8 @@ nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) asyc->set.dither = true; } } else { - asyc->set.mask = ~0; + if (asyc) + asyc->set.mask = ~0; asyh->set.mask = ~0; } @@ -2391,6 +2403,51 @@ out: /****************************************************************************** * Output path helpers *****************************************************************************/ +static void +nv50_outp_release(struct nouveau_encoder *nv_encoder) +{ + struct nv50_disp *disp = nv50_disp(nv_encoder->base.base.dev); + struct { + struct nv50_disp_mthd_v1 base; + } args = { + .base.version = 1, + .base.method = NV50_DISP_MTHD_V1_RELEASE, + .base.hasht = nv_encoder->dcb->hasht, + .base.hashm = nv_encoder->dcb->hashm, + }; + + nvif_mthd(disp->disp, 0, &args, sizeof(args)); + nv_encoder->or = -1; + nv_encoder->link = 0; +} + +static int +nv50_outp_acquire(struct nouveau_encoder *nv_encoder) +{ + struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); + struct nv50_disp *disp = nv50_disp(drm->dev); + struct { + struct nv50_disp_mthd_v1 base; + struct nv50_disp_acquire_v0 info; + } args = { + .base.version = 1, + .base.method = NV50_DISP_MTHD_V1_ACQUIRE, + .base.hasht = nv_encoder->dcb->hasht, + .base.hashm = nv_encoder->dcb->hashm, + }; + int ret; + + ret = nvif_mthd(disp->disp, 0, &args, sizeof(args)); + if (ret) { + NV_ERROR(drm, "error acquiring output path: %d\n", ret); + return ret; + } + + nv_encoder->or = args.info.or; + nv_encoder->link = args.info.link; + return 0; +} + static int nv50_outp_atomic_check_view(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state, @@ -2448,30 +2505,6 @@ nv50_outp_atomic_check(struct drm_encoder *encoder, * DAC *****************************************************************************/ static void -nv50_dac_dpms(struct drm_encoder *encoder, int mode) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nv50_disp *disp = nv50_disp(encoder->dev); - struct { - struct nv50_disp_mthd_v1 base; - struct nv50_disp_dac_pwr_v0 pwr; - } args = { - .base.version = 1, - .base.method = NV50_DISP_MTHD_V1_DAC_PWR, - .base.hasht = nv_encoder->dcb->hasht, - .base.hashm = nv_encoder->dcb->hashm, - .pwr.state = 1, - .pwr.data = 1, - .pwr.vsync = (mode != DRM_MODE_DPMS_SUSPEND && - mode != DRM_MODE_DPMS_OFF), - .pwr.hsync = (mode != DRM_MODE_DPMS_STANDBY && - mode != DRM_MODE_DPMS_OFF), - }; - - nvif_mthd(disp->disp, 0, &args, sizeof(args)); -} - -static void nv50_dac_disable(struct drm_encoder *encoder) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); @@ -2494,6 +2527,7 @@ nv50_dac_disable(struct drm_encoder *encoder) } nv_encoder->crtc = NULL; + nv50_outp_release(nv_encoder); } static void @@ -2505,6 +2539,8 @@ nv50_dac_enable(struct drm_encoder *encoder) struct drm_display_mode *mode = &nv_crtc->base.state->adjusted_mode; u32 *push; + nv50_outp_acquire(nv_encoder); + push = evo_wait(mast, 8); if (push) { if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) { @@ -2572,7 +2608,6 @@ nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) static const struct drm_encoder_helper_funcs nv50_dac_help = { - .dpms = nv50_dac_dpms, .atomic_check = nv50_outp_atomic_check, .enable = nv50_dac_enable, .disable = nv50_dac_disable, @@ -2605,7 +2640,6 @@ nv50_dac_create(struct drm_connector *connector, struct dcb_output *dcbe) if (!nv_encoder) return -ENOMEM; nv_encoder->dcb = dcbe; - nv_encoder->or = ffs(dcbe->or) - 1; bus = nvkm_i2c_bus_find(i2c, dcbe->i2c_index); if (bus) @@ -2707,6 +2741,7 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) struct { struct nv50_disp_mthd_v1 base; struct nv50_disp_sor_hdmi_pwr_v0 pwr; + u8 infoframes[2 * 17]; /* two frames, up to 17 bytes each */ } args = { .base.version = 1, .base.method = NV50_DISP_MTHD_V1_SOR_HDMI_PWR, @@ -2718,17 +2753,42 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) }; struct nouveau_connector *nv_connector; u32 max_ac_packet; + union hdmi_infoframe avi_frame; + union hdmi_infoframe vendor_frame; + int ret; + int size; nv_connector = nouveau_encoder_connector_get(nv_encoder); if (!drm_detect_hdmi_monitor(nv_connector->edid)) return; + ret = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame.avi, mode); + if (!ret) { + /* We have an AVI InfoFrame, populate it to the display */ + args.pwr.avi_infoframe_length + = hdmi_infoframe_pack(&avi_frame, args.infoframes, 17); + } + + ret = drm_hdmi_vendor_infoframe_from_display_mode(&vendor_frame.vendor.hdmi, mode); + if (!ret) { + /* We have a Vendor InfoFrame, populate it to the display */ + args.pwr.vendor_infoframe_length + = hdmi_infoframe_pack(&vendor_frame, + args.infoframes + + args.pwr.avi_infoframe_length, + 17); + } + max_ac_packet = mode->htotal - mode->hdisplay; max_ac_packet -= args.pwr.rekey; max_ac_packet -= 18; /* constant from tegra */ args.pwr.max_ac_packet = max_ac_packet / 32; - nvif_mthd(disp->disp, 0, &args, sizeof(args)); + size = sizeof(args.base) + + sizeof(args.pwr) + + args.pwr.avi_infoframe_length + + args.pwr.vendor_infoframe_length; + nvif_mthd(disp->disp, 0, &args, size); nv50_audio_enable(encoder, mode); } @@ -2746,6 +2806,8 @@ struct nv50_mstm { struct nv50_msto *msto[4]; bool modified; + bool disabled; + int links; }; struct nv50_mstc { @@ -2894,7 +2956,10 @@ nv50_msto_enable(struct drm_encoder *encoder) r = drm_dp_mst_allocate_vcpi(&mstm->mgr, mstc->port, mstc->pbn, slots); WARN_ON(!r); - if (mstm->outp->dcb->sorconf.link & 1) + if (!mstm->links++) + nv50_outp_acquire(mstm->outp); + + if (mstm->outp->link & 1) proto = 0x8; else proto = 0x9; @@ -2926,6 +2991,8 @@ nv50_msto_disable(struct drm_encoder *encoder) mstm->outp->update(mstm->outp, msto->head->base.index, NULL, 0, 0); mstm->modified = true; + if (!--mstm->links) + mstm->disabled = true; msto->disabled = true; } @@ -3141,6 +3208,12 @@ nv50_mstm_prepare(struct nv50_mstm *mstm) nv50_msto_prepare(msto); } } + + if (mstm->disabled) { + if (!mstm->links) + nv50_outp_release(mstm->outp); + mstm->disabled = false; + } } static void @@ -3368,25 +3441,6 @@ nv50_mstm_new(struct nouveau_encoder *outp, struct drm_dp_aux *aux, int aux_max, * SOR *****************************************************************************/ static void -nv50_sor_dpms(struct drm_encoder *encoder, int mode) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nv50_disp *disp = nv50_disp(encoder->dev); - struct { - struct nv50_disp_mthd_v1 base; - struct nv50_disp_sor_pwr_v0 pwr; - } args = { - .base.version = 1, - .base.method = NV50_DISP_MTHD_V1_SOR_PWR, - .base.hasht = nv_encoder->dcb->hasht, - .base.hashm = nv_encoder->dcb->hashm, - .pwr.state = mode == DRM_MODE_DPMS_ON, - }; - - nvif_mthd(disp->disp, 0, &args, sizeof(args)); -} - -static void nv50_sor_update(struct nouveau_encoder *nv_encoder, u8 head, struct drm_display_mode *mode, u8 proto, u8 depth) { @@ -3458,6 +3512,7 @@ nv50_sor_disable(struct drm_encoder *encoder) nv_encoder->update(nv_encoder, nv_crtc->index, NULL, 0, 0); nv50_audio_disable(encoder, nv_crtc); nv50_hdmi_disable(&nv_encoder->base.base, nv_crtc); + nv50_outp_release(nv_encoder); } } @@ -3486,10 +3541,11 @@ nv50_sor_enable(struct drm_encoder *encoder) nv_connector = nouveau_encoder_connector_get(nv_encoder); nv_encoder->crtc = encoder->crtc; + nv50_outp_acquire(nv_encoder); switch (nv_encoder->dcb->type) { case DCB_OUTPUT_TMDS: - if (nv_encoder->dcb->sorconf.link & 1) { + if (nv_encoder->link & 1) { proto = 0x1; /* Only enable dual-link if: * - Need to (i.e. rate > 165MHz) @@ -3547,7 +3603,7 @@ nv50_sor_enable(struct drm_encoder *encoder) else depth = 0x6; - if (nv_encoder->dcb->sorconf.link & 1) + if (nv_encoder->link & 1) proto = 0x8; else proto = 0x9; @@ -3564,7 +3620,6 @@ nv50_sor_enable(struct drm_encoder *encoder) static const struct drm_encoder_helper_funcs nv50_sor_help = { - .dpms = nv50_sor_dpms, .atomic_check = nv50_outp_atomic_check, .enable = nv50_sor_enable, .disable = nv50_sor_disable, @@ -3607,7 +3662,6 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) if (!nv_encoder) return -ENOMEM; nv_encoder->dcb = dcbe; - nv_encoder->or = ffs(dcbe->or) - 1; nv_encoder->update = nv50_sor_update; encoder = to_drm_encoder(nv_encoder); @@ -3648,26 +3702,6 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) /****************************************************************************** * PIOR *****************************************************************************/ -static void -nv50_pior_dpms(struct drm_encoder *encoder, int mode) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nv50_disp *disp = nv50_disp(encoder->dev); - struct { - struct nv50_disp_mthd_v1 base; - struct nv50_disp_pior_pwr_v0 pwr; - } args = { - .base.version = 1, - .base.method = NV50_DISP_MTHD_V1_PIOR_PWR, - .base.hasht = nv_encoder->dcb->hasht, - .base.hashm = nv_encoder->dcb->hashm, - .pwr.state = mode == DRM_MODE_DPMS_ON, - .pwr.type = nv_encoder->dcb->type, - }; - - nvif_mthd(disp->disp, 0, &args, sizeof(args)); -} - static int nv50_pior_atomic_check(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state, @@ -3700,6 +3734,7 @@ nv50_pior_disable(struct drm_encoder *encoder) } nv_encoder->crtc = NULL; + nv50_outp_release(nv_encoder); } static void @@ -3714,6 +3749,8 @@ nv50_pior_enable(struct drm_encoder *encoder) u8 proto, depth; u32 *push; + nv50_outp_acquire(nv_encoder); + nv_connector = nouveau_encoder_connector_get(nv_encoder); switch (nv_connector->base.display_info.bpc) { case 10: depth = 0x6; break; @@ -3752,7 +3789,6 @@ nv50_pior_enable(struct drm_encoder *encoder) static const struct drm_encoder_helper_funcs nv50_pior_help = { - .dpms = nv50_pior_dpms, .atomic_check = nv50_pior_atomic_check, .enable = nv50_pior_enable, .disable = nv50_pior_disable, @@ -3802,7 +3838,6 @@ nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe) if (!nv_encoder) return -ENOMEM; nv_encoder->dcb = dcbe; - nv_encoder->or = ffs(dcbe->or) - 1; nv_encoder->i2c = ddc; nv_encoder->aux = aux; @@ -4317,14 +4352,8 @@ nv50_display_init(struct drm_device *dev) list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { - const struct drm_encoder_helper_funcs *help; - struct nouveau_encoder *nv_encoder; - - nv_encoder = nouveau_encoder(encoder); - help = encoder->helper_private; - if (help && help->dpms) - help->dpms(encoder, DRM_MODE_DPMS_ON); - + struct nouveau_encoder *nv_encoder = + nouveau_encoder(encoder); nv50_mstm_init(nv_encoder->dp.mstm); } } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c index b690bc12a5b7..7bdc7a5ae723 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c @@ -1257,7 +1257,7 @@ nvaa_chipset = { .therm = g84_therm_new, .timer = nv41_timer_new, .volt = nv40_volt_new, - .disp = g94_disp_new, + .disp = mcp77_disp_new, .dma = nv50_dma_new, .fifo = g84_fifo_new, .gr = gt200_gr_new, @@ -1289,7 +1289,7 @@ nvac_chipset = { .therm = g84_therm_new, .timer = nv41_timer_new, .volt = nv40_volt_new, - .disp = g94_disp_new, + .disp = mcp77_disp_new, .dma = nv50_dma_new, .fifo = g84_fifo_new, .gr = mcp79_gr_new, @@ -1323,7 +1323,7 @@ nvaf_chipset = { .timer = nv41_timer_new, .volt = nv40_volt_new, .ce[0] = gt215_ce_new, - .disp = gt215_disp_new, + .disp = mcp89_disp_new, .dma = nv50_dma_new, .fifo = g84_fifo_new, .gr = mcp89_gr_new, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c index 6474bd2a6d07..189ed80e21ff 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c @@ -51,10 +51,12 @@ nvkm_device_tegra_power_up(struct nvkm_device_tegra *tdev) reset_control_assert(tdev->rst); udelay(10); - ret = tegra_powergate_remove_clamping(TEGRA_POWERGATE_3D); - if (ret) - goto err_clamp; - udelay(10); + if (!tdev->pdev->dev.pm_domain) { + ret = tegra_powergate_remove_clamping(TEGRA_POWERGATE_3D); + if (ret) + goto err_clamp; + udelay(10); + } reset_control_deassert(tdev->rst); udelay(10); @@ -80,9 +82,6 @@ nvkm_device_tegra_power_down(struct nvkm_device_tegra *tdev) { int ret; - reset_control_assert(tdev->rst); - udelay(10); - clk_disable_unprepare(tdev->clk_pwr); if (tdev->clk_ref) clk_disable_unprepare(tdev->clk_ref); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild index fa05d16ae948..48ce6699183e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild @@ -4,7 +4,9 @@ nvkm-y += nvkm/engine/disp/nv50.o nvkm-y += nvkm/engine/disp/g84.o nvkm-y += nvkm/engine/disp/g94.o nvkm-y += nvkm/engine/disp/gt200.o +nvkm-y += nvkm/engine/disp/mcp77.o nvkm-y += nvkm/engine/disp/gt215.o +nvkm-y += nvkm/engine/disp/mcp89.o nvkm-y += nvkm/engine/disp/gf119.o nvkm-y += nvkm/engine/disp/gk104.o nvkm-y += nvkm/engine/disp/gk110.o @@ -12,29 +14,41 @@ nvkm-y += nvkm/engine/disp/gm107.o nvkm-y += nvkm/engine/disp/gm200.o nvkm-y += nvkm/engine/disp/gp100.o nvkm-y += nvkm/engine/disp/gp102.o +nvkm-y += nvkm/engine/disp/vga.o -nvkm-y += nvkm/engine/disp/outp.o -nvkm-y += nvkm/engine/disp/outpdp.o +nvkm-y += nvkm/engine/disp/head.o +nvkm-y += nvkm/engine/disp/headnv04.o +nvkm-y += nvkm/engine/disp/headnv50.o +nvkm-y += nvkm/engine/disp/headgf119.o + +nvkm-y += nvkm/engine/disp/ior.o nvkm-y += nvkm/engine/disp/dacnv50.o +nvkm-y += nvkm/engine/disp/dacgf119.o nvkm-y += nvkm/engine/disp/piornv50.o nvkm-y += nvkm/engine/disp/sornv50.o +nvkm-y += nvkm/engine/disp/sorg84.o nvkm-y += nvkm/engine/disp/sorg94.o +nvkm-y += nvkm/engine/disp/sormcp77.o +nvkm-y += nvkm/engine/disp/sorgt215.o +nvkm-y += nvkm/engine/disp/sormcp89.o nvkm-y += nvkm/engine/disp/sorgf119.o +nvkm-y += nvkm/engine/disp/sorgk104.o nvkm-y += nvkm/engine/disp/sorgm107.o nvkm-y += nvkm/engine/disp/sorgm200.o -nvkm-y += nvkm/engine/disp/dport.o -nvkm-y += nvkm/engine/disp/conn.o +nvkm-y += nvkm/engine/disp/outp.o +nvkm-y += nvkm/engine/disp/dp.o nvkm-y += nvkm/engine/disp/hdagt215.o nvkm-y += nvkm/engine/disp/hdagf119.o +nvkm-y += nvkm/engine/disp/hdmi.o nvkm-y += nvkm/engine/disp/hdmig84.o nvkm-y += nvkm/engine/disp/hdmigt215.o nvkm-y += nvkm/engine/disp/hdmigf119.o nvkm-y += nvkm/engine/disp/hdmigk104.o -nvkm-y += nvkm/engine/disp/vga.o +nvkm-y += nvkm/engine/disp/conn.o nvkm-y += nvkm/engine/disp/rootnv04.o nvkm-y += nvkm/engine/disp/rootnv50.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c index 1efe91b1e22b..c7c84d34d97e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c @@ -23,6 +23,9 @@ */ #include "priv.h" #include "conn.h" +#include "dp.h" +#include "head.h" +#include "ior.h" #include "outp.h" #include <core/client.h> @@ -37,17 +40,21 @@ #include <nvif/unpack.h> static void -nvkm_disp_vblank_fini(struct nvkm_event *event, int type, int head) +nvkm_disp_vblank_fini(struct nvkm_event *event, int type, int id) { struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank); - disp->func->head.vblank_fini(disp, head); + struct nvkm_head *head = nvkm_head_find(disp, id); + if (head) + head->func->vblank_put(head); } static void -nvkm_disp_vblank_init(struct nvkm_event *event, int type, int head) +nvkm_disp_vblank_init(struct nvkm_event *event, int type, int id) { struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank); - disp->func->head.vblank_init(disp, head); + struct nvkm_head *head = nvkm_head_find(disp, id); + if (head) + head->func->vblank_get(head); } static int @@ -96,7 +103,7 @@ nvkm_disp_hpd_ctor(struct nvkm_object *object, void *data, u32 size, union { struct nvif_notify_conn_req_v0 v0; } *req = data; - struct nvkm_output *outp; + struct nvkm_outp *outp; int ret = -ENOSYS; if (!(ret = nvif_unpack(ret, &data, &size, req->v0, 0, 0, false))) { @@ -210,15 +217,15 @@ static int nvkm_disp_fini(struct nvkm_engine *engine, bool suspend) { struct nvkm_disp *disp = nvkm_disp(engine); - struct nvkm_connector *conn; - struct nvkm_output *outp; + struct nvkm_conn *conn; + struct nvkm_outp *outp; list_for_each_entry(outp, &disp->outp, head) { - nvkm_output_fini(outp); + nvkm_outp_fini(outp); } list_for_each_entry(conn, &disp->conn, head) { - nvkm_connector_fini(conn); + nvkm_conn_fini(conn); } return 0; @@ -228,129 +235,70 @@ static int nvkm_disp_init(struct nvkm_engine *engine) { struct nvkm_disp *disp = nvkm_disp(engine); - struct nvkm_connector *conn; - struct nvkm_output *outp; + struct nvkm_conn *conn; + struct nvkm_outp *outp; list_for_each_entry(conn, &disp->conn, head) { - nvkm_connector_init(conn); + nvkm_conn_init(conn); } list_for_each_entry(outp, &disp->outp, head) { - nvkm_output_init(outp); + nvkm_outp_init(outp); } return 0; } -static void * -nvkm_disp_dtor(struct nvkm_engine *engine) +static int +nvkm_disp_oneinit(struct nvkm_engine *engine) { struct nvkm_disp *disp = nvkm_disp(engine); - struct nvkm_connector *conn; - struct nvkm_output *outp; - void *data = disp; - - if (disp->func->dtor) - data = disp->func->dtor(disp); - - nvkm_event_fini(&disp->vblank); - nvkm_event_fini(&disp->hpd); - - while (!list_empty(&disp->outp)) { - outp = list_first_entry(&disp->outp, typeof(*outp), head); - list_del(&outp->head); - nvkm_output_del(&outp); - } - - while (!list_empty(&disp->conn)) { - conn = list_first_entry(&disp->conn, typeof(*conn), head); - list_del(&conn->head); - nvkm_connector_del(&conn); - } - - return data; -} - -static const struct nvkm_engine_func -nvkm_disp = { - .dtor = nvkm_disp_dtor, - .init = nvkm_disp_init, - .fini = nvkm_disp_fini, - .intr = nvkm_disp_intr, - .base.sclass = nvkm_disp_class_get, -}; - -int -nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device, - int index, int heads, struct nvkm_disp *disp) -{ - struct nvkm_bios *bios = device->bios; - struct nvkm_output *outp, *outt, *pair; - struct nvkm_connector *conn; + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_bios *bios = subdev->device->bios; + struct nvkm_outp *outp, *outt, *pair; + struct nvkm_conn *conn; + struct nvkm_head *head; struct nvbios_connE connE; struct dcb_output dcbE; u8 hpd = 0, ver, hdr; u32 data; int ret, i; - INIT_LIST_HEAD(&disp->outp); - INIT_LIST_HEAD(&disp->conn); - disp->func = func; - disp->head.nr = heads; - - ret = nvkm_engine_ctor(&nvkm_disp, device, index, true, &disp->engine); - if (ret) - return ret; - - /* create output objects for each display path in the vbios */ + /* Create output path objects for each VBIOS display path. */ i = -1; while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) { - const struct nvkm_disp_func_outp *outps; - int (*ctor)(struct nvkm_disp *, int, struct dcb_output *, - struct nvkm_output **); - if (dcbE.type == DCB_OUTPUT_UNUSED) continue; if (dcbE.type == DCB_OUTPUT_EOL) break; outp = NULL; - switch (dcbE.location) { - case 0: outps = &disp->func->outp.internal; break; - case 1: outps = &disp->func->outp.external; break; - default: - nvkm_warn(&disp->engine.subdev, - "dcb %d locn %d unknown\n", i, dcbE.location); - continue; - } - switch (dcbE.type) { - case DCB_OUTPUT_ANALOG: ctor = outps->crt ; break; - case DCB_OUTPUT_TV : ctor = outps->tv ; break; - case DCB_OUTPUT_TMDS : ctor = outps->tmds; break; - case DCB_OUTPUT_LVDS : ctor = outps->lvds; break; - case DCB_OUTPUT_DP : ctor = outps->dp ; break; + case DCB_OUTPUT_ANALOG: + case DCB_OUTPUT_TV: + case DCB_OUTPUT_TMDS: + case DCB_OUTPUT_LVDS: + ret = nvkm_outp_new(disp, i, &dcbE, &outp); + break; + case DCB_OUTPUT_DP: + ret = nvkm_dp_new(disp, i, &dcbE, &outp); + break; default: - nvkm_warn(&disp->engine.subdev, - "dcb %d type %d unknown\n", i, dcbE.type); + nvkm_warn(subdev, "dcb %d type %d unknown\n", + i, dcbE.type); continue; } - if (ctor) - ret = ctor(disp, i, &dcbE, &outp); - else - ret = -ENODEV; - if (ret) { - if (ret == -ENODEV) { - nvkm_debug(&disp->engine.subdev, - "dcb %d %d/%d not supported\n", - i, dcbE.location, dcbE.type); + if (outp) { + if (ret != -ENODEV) + OUTP_ERR(outp, "ctor failed: %d", ret); + else + OUTP_DBG(outp, "not supported"); + nvkm_outp_del(&outp); continue; } - nvkm_error(&disp->engine.subdev, - "failed to create output %d\n", i); - nvkm_output_del(&outp); + nvkm_error(subdev, "failed to create outp %d\n", i); continue; } @@ -358,18 +306,18 @@ nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device, hpd = max(hpd, (u8)(dcbE.connector + 1)); } - /* create connector objects based on the outputs we support */ + /* Create connector objects based on available output paths. */ list_for_each_entry_safe(outp, outt, &disp->outp, head) { - /* bios data *should* give us the most useful information */ + /* VBIOS data *should* give us the most useful information. */ data = nvbios_connEp(bios, outp->info.connector, &ver, &hdr, &connE); - /* no bios connector data... */ + /* No bios connector data... */ if (!data) { - /* heuristic: anything with the same ccb index is + /* Heuristic: anything with the same ccb index is * considered to be on the same connector, any * output path without an associated ccb entry will - * be put on its own connector + * be put on its own connector. */ int ccb_index = outp->info.i2c_index; if (ccb_index != 0xf) { @@ -381,7 +329,7 @@ nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device, } } - /* connector shared with another output path */ + /* Connector shared with another output path. */ if (outp->conn) continue; @@ -392,7 +340,7 @@ nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device, i = outp->info.connector; } - /* check that we haven't already created this connector */ + /* Check that we haven't already created this connector. */ list_for_each_entry(conn, &disp->conn, head) { if (conn->index == outp->info.connector) { outp->conn = conn; @@ -403,15 +351,15 @@ nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device, if (outp->conn) continue; - /* apparently we need to create a new one! */ - ret = nvkm_connector_new(disp, i, &connE, &outp->conn); + /* Apparently we need to create a new one! */ + ret = nvkm_conn_new(disp, i, &connE, &outp->conn); if (ret) { nvkm_error(&disp->engine.subdev, - "failed to create output %d conn: %d\n", + "failed to create outp %d conn: %d\n", outp->index, ret); - nvkm_connector_del(&outp->conn); + nvkm_conn_del(&outp->conn); list_del(&outp->head); - nvkm_output_del(&outp); + nvkm_outp_del(&outp); continue; } @@ -422,18 +370,81 @@ nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device, if (ret) return ret; - ret = nvkm_event_init(&nvkm_disp_vblank_func, 1, heads, &disp->vblank); - if (ret) - return ret; + i = 0; + list_for_each_entry(head, &disp->head, head) + i = max(i, head->id + 1); - return 0; + return nvkm_event_init(&nvkm_disp_vblank_func, 1, i, &disp->vblank); +} + +static void * +nvkm_disp_dtor(struct nvkm_engine *engine) +{ + struct nvkm_disp *disp = nvkm_disp(engine); + struct nvkm_conn *conn; + struct nvkm_outp *outp; + void *data = disp; + + if (disp->func->dtor) + data = disp->func->dtor(disp); + + nvkm_event_fini(&disp->vblank); + nvkm_event_fini(&disp->hpd); + + while (!list_empty(&disp->conn)) { + conn = list_first_entry(&disp->conn, typeof(*conn), head); + list_del(&conn->head); + nvkm_conn_del(&conn); + } + + while (!list_empty(&disp->outp)) { + outp = list_first_entry(&disp->outp, typeof(*outp), head); + list_del(&outp->head); + nvkm_outp_del(&outp); + } + + while (!list_empty(&disp->ior)) { + struct nvkm_ior *ior = + list_first_entry(&disp->ior, typeof(*ior), head); + nvkm_ior_del(&ior); + } + + while (!list_empty(&disp->head)) { + struct nvkm_head *head = + list_first_entry(&disp->head, typeof(*head), head); + nvkm_head_del(&head); + } + + return data; +} + +static const struct nvkm_engine_func +nvkm_disp = { + .dtor = nvkm_disp_dtor, + .oneinit = nvkm_disp_oneinit, + .init = nvkm_disp_init, + .fini = nvkm_disp_fini, + .intr = nvkm_disp_intr, + .base.sclass = nvkm_disp_class_get, +}; + +int +nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device, + int index, struct nvkm_disp *disp) +{ + disp->func = func; + INIT_LIST_HEAD(&disp->head); + INIT_LIST_HEAD(&disp->ior); + INIT_LIST_HEAD(&disp->outp); + INIT_LIST_HEAD(&disp->conn); + return nvkm_engine_ctor(&nvkm_disp, device, index, true, &disp->engine); } int nvkm_disp_new_(const struct nvkm_disp_func *func, struct nvkm_device *device, - int index, int heads, struct nvkm_disp **pdisp) + int index, struct nvkm_disp **pdisp) { if (!(*pdisp = kzalloc(sizeof(**pdisp), GFP_KERNEL))) return -ENOMEM; - return nvkm_disp_ctor(func, device, index, heads, *pdisp); + return nvkm_disp_ctor(func, device, index, *pdisp); } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/basenv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/basenv50.c index 83f152300ec0..f1d6b820d482 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/basenv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/basenv50.c @@ -22,6 +22,7 @@ * Authors: Ben Skeggs */ #include "dmacnv50.h" +#include "head.h" #include "rootnv50.h" #include <core/client.h> @@ -50,7 +51,7 @@ nv50_disp_base_new(const struct nv50_disp_dmac_func *func, nvif_ioctl(parent, "create disp base channel dma vers %d " "pushbuf %016llx head %d\n", args->v0.version, args->v0.pushbuf, args->v0.head); - if (args->v0.head > disp->base.head.nr) + if (!nvkm_head_find(&disp->base, args->v0.head)) return -EINVAL; push = args->v0.pushbuf; head = args->v0.head; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c index 524a24eae1a0..0c0310498afd 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c @@ -25,6 +25,7 @@ #include "rootnv50.h" #include <core/client.h> +#include <core/notify.h> #include <core/ramht.h> #include <engine/dma.h> diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c index c6910d644a3d..febc5c274488 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c @@ -30,9 +30,9 @@ #include <nvif/event.h> static int -nvkm_connector_hpd(struct nvkm_notify *notify) +nvkm_conn_hpd(struct nvkm_notify *notify) { - struct nvkm_connector *conn = container_of(notify, typeof(*conn), hpd); + struct nvkm_conn *conn = container_of(notify, typeof(*conn), hpd); struct nvkm_disp *disp = conn->disp; struct nvkm_gpio *gpio = disp->engine.subdev.device->gpio; const struct nvkm_gpio_ntfy_rep *line = notify->data; @@ -52,21 +52,21 @@ nvkm_connector_hpd(struct nvkm_notify *notify) } void -nvkm_connector_fini(struct nvkm_connector *conn) +nvkm_conn_fini(struct nvkm_conn *conn) { nvkm_notify_put(&conn->hpd); } void -nvkm_connector_init(struct nvkm_connector *conn) +nvkm_conn_init(struct nvkm_conn *conn) { nvkm_notify_get(&conn->hpd); } void -nvkm_connector_del(struct nvkm_connector **pconn) +nvkm_conn_del(struct nvkm_conn **pconn) { - struct nvkm_connector *conn = *pconn; + struct nvkm_conn *conn = *pconn; if (conn) { nvkm_notify_fini(&conn->hpd); kfree(*pconn); @@ -75,8 +75,8 @@ nvkm_connector_del(struct nvkm_connector **pconn) } static void -nvkm_connector_ctor(struct nvkm_disp *disp, int index, - struct nvbios_connE *info, struct nvkm_connector *conn) +nvkm_conn_ctor(struct nvkm_disp *disp, int index, struct nvbios_connE *info, + struct nvkm_conn *conn) { static const u8 hpd[] = { 0x07, 0x08, 0x51, 0x52, 0x5e, 0x5f, 0x60 }; struct nvkm_gpio *gpio = disp->engine.subdev.device->gpio; @@ -105,7 +105,7 @@ nvkm_connector_ctor(struct nvkm_disp *disp, int index, return; } - ret = nvkm_notify_init(NULL, &gpio->event, nvkm_connector_hpd, + ret = nvkm_notify_init(NULL, &gpio->event, nvkm_conn_hpd, true, &(struct nvkm_gpio_ntfy_req) { .mask = NVKM_GPIO_TOGGLED, .line = func.line, @@ -122,11 +122,11 @@ nvkm_connector_ctor(struct nvkm_disp *disp, int index, } int -nvkm_connector_new(struct nvkm_disp *disp, int index, - struct nvbios_connE *info, struct nvkm_connector **pconn) +nvkm_conn_new(struct nvkm_disp *disp, int index, struct nvbios_connE *info, + struct nvkm_conn **pconn) { if (!(*pconn = kzalloc(sizeof(**pconn), GFP_KERNEL))) return -ENOMEM; - nvkm_connector_ctor(disp, index, info, *pconn); + nvkm_conn_ctor(disp, index, info, *pconn); return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h index ed32fe7f1864..de962b7b026d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h @@ -6,7 +6,7 @@ #include <subdev/bios.h> #include <subdev/bios/conn.h> -struct nvkm_connector { +struct nvkm_conn { struct nvkm_disp *disp; int index; struct nvbios_connE info; @@ -16,14 +16,14 @@ struct nvkm_connector { struct list_head head; }; -int nvkm_connector_new(struct nvkm_disp *, int index, struct nvbios_connE *, - struct nvkm_connector **); -void nvkm_connector_del(struct nvkm_connector **); -void nvkm_connector_init(struct nvkm_connector *); -void nvkm_connector_fini(struct nvkm_connector *); +int nvkm_conn_new(struct nvkm_disp *, int index, struct nvbios_connE *, + struct nvkm_conn **); +void nvkm_conn_del(struct nvkm_conn **); +void nvkm_conn_init(struct nvkm_conn *); +void nvkm_conn_fini(struct nvkm_conn *); #define CONN_MSG(c,l,f,a...) do { \ - struct nvkm_connector *_conn = (c); \ + struct nvkm_conn *_conn = (c); \ nvkm_##l(&_conn->disp->engine.subdev, "conn %02x:%02x%02x: "f"\n", \ _conn->index, _conn->info.location, _conn->info.type, ##a); \ } while(0) diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/cursnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/cursnv50.c index 82ff82d8c1ab..ab51121b7982 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/cursnv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/cursnv50.c @@ -22,6 +22,7 @@ * Authors: Ben Skeggs */ #include "channv50.h" +#include "head.h" #include "rootnv50.h" #include <core/client.h> @@ -48,7 +49,7 @@ nv50_disp_curs_new(const struct nv50_disp_chan_func *func, if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { nvif_ioctl(parent, "create disp cursor vers %d head %d\n", args->v0.version, args->v0.head); - if (args->v0.head > disp->base.head.nr) + if (!nvkm_head_find(&disp->base, args->v0.head)) return -EINVAL; head = args->v0.head; } else diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacgf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacgf119.c new file mode 100644 index 000000000000..dbd032ef352a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacgf119.c @@ -0,0 +1,65 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * 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 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 HOLDER(S) OR AUTHOR(S) 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 "ior.h" + +static void +gf119_dac_clock(struct nvkm_ior *dac) +{ + struct nvkm_device *device = dac->disp->engine.subdev.device; + const u32 doff = nv50_ior_base(dac); + nvkm_mask(device, 0x612280 + doff, 0x07070707, 0x00000000); +} + +static void +gf119_dac_state(struct nvkm_ior *dac, struct nvkm_ior_state *state) +{ + struct nvkm_device *device = dac->disp->engine.subdev.device; + const u32 coff = (state == &dac->asy) * 0x20000 + dac->id * 0x20; + u32 ctrl = nvkm_rd32(device, 0x640180 + coff); + + state->proto_evo = (ctrl & 0x00000f00) >> 8; + switch (state->proto_evo) { + case 0: state->proto = CRT; break; + default: + state->proto = UNKNOWN; + break; + } + + state->head = ctrl & 0x0000000f; +} + +static const struct nvkm_ior_func +gf119_dac = { + .state = gf119_dac_state, + .power = nv50_dac_power, + .sense = nv50_dac_sense, + .clock = gf119_dac_clock, +}; + +int +gf119_dac_new(struct nvkm_disp *disp, int id) +{ + struct nvkm_device *device = disp->engine.subdev.device; + if (!(nvkm_rd32(device, 0x612004) & (0x00000010 << id))) + return 0; + return nvkm_ior_new_(&gf119_dac, disp, DAC, id); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacnv50.c index c9b78b8f9c87..85e692b12260 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacnv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacnv50.c @@ -21,106 +21,96 @@ * * Authors: Ben Skeggs */ -#include "nv50.h" -#include "outp.h" +#include "ior.h" -#include <core/client.h> #include <subdev/timer.h> -#include <nvif/cl5070.h> -#include <nvif/unpack.h> - -int -nv50_dac_power(NV50_DISP_MTHD_V1) +static void +nv50_dac_clock(struct nvkm_ior *dac) { - struct nvkm_device *device = disp->base.engine.subdev.device; - const u32 doff = outp->or * 0x800; - union { - struct nv50_disp_dac_pwr_v0 v0; - } *args = data; - u32 stat; - int ret = -ENOSYS; - - nvif_ioctl(object, "disp dac pwr size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { - nvif_ioctl(object, "disp dac pwr vers %d state %d data %d " - "vsync %d hsync %d\n", - args->v0.version, args->v0.state, args->v0.data, - args->v0.vsync, args->v0.hsync); - stat = 0x00000040 * !args->v0.state; - stat |= 0x00000010 * !args->v0.data; - stat |= 0x00000004 * !args->v0.vsync; - stat |= 0x00000001 * !args->v0.hsync; - } else - return ret; - - nvkm_msec(device, 2000, - if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000)) - break; - ); - nvkm_mask(device, 0x61a004 + doff, 0xc000007f, 0x80000000 | stat); - nvkm_msec(device, 2000, - if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000)) - break; - ); - return 0; + struct nvkm_device *device = dac->disp->engine.subdev.device; + const u32 doff = nv50_ior_base(dac); + nvkm_mask(device, 0x614280 + doff, 0x07070707, 0x00000000); } int -nv50_dac_sense(NV50_DISP_MTHD_V1) +nv50_dac_sense(struct nvkm_ior *dac, u32 loadval) { - struct nvkm_subdev *subdev = &disp->base.engine.subdev; - struct nvkm_device *device = subdev->device; - union { - struct nv50_disp_dac_load_v0 v0; - } *args = data; - const u32 doff = outp->or * 0x800; - u32 loadval; - int ret = -ENOSYS; + struct nvkm_device *device = dac->disp->engine.subdev.device; + const u32 doff = nv50_ior_base(dac); - nvif_ioctl(object, "disp dac load size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { - nvif_ioctl(object, "disp dac load vers %d data %08x\n", - args->v0.version, args->v0.data); - if (args->v0.data & 0xfff00000) - return -EINVAL; - loadval = args->v0.data; - } else - return ret; - - nvkm_mask(device, 0x61a004 + doff, 0x807f0000, 0x80150000); - nvkm_msec(device, 2000, - if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000)) - break; - ); + dac->func->power(dac, false, true, false, false, false); nvkm_wr32(device, 0x61a00c + doff, 0x00100000 | loadval); mdelay(9); udelay(500); loadval = nvkm_mask(device, 0x61a00c + doff, 0xffffffff, 0x00000000); - nvkm_mask(device, 0x61a004 + doff, 0x807f0000, 0x80550000); + dac->func->power(dac, false, false, false, false, false); + if (!(loadval & 0x80000000)) + return -ETIMEDOUT; + + return (loadval & 0x38000000) >> 27; +} + +static void +nv50_dac_power_wait(struct nvkm_device *device, const u32 doff) +{ nvkm_msec(device, 2000, if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000)) break; ); +} - nvkm_debug(subdev, "DAC%d sense: %08x\n", outp->or, loadval); - if (!(loadval & 0x80000000)) - return -ETIMEDOUT; +void +nv50_dac_power(struct nvkm_ior *dac, bool normal, bool pu, + bool data, bool vsync, bool hsync) +{ + struct nvkm_device *device = dac->disp->engine.subdev.device; + const u32 doff = nv50_ior_base(dac); + const u32 shift = normal ? 0 : 16; + const u32 state = 0x80000000 | (0x00000040 * ! pu | + 0x00000010 * ! data | + 0x00000004 * ! vsync | + 0x00000001 * ! hsync) << shift; + const u32 field = 0xc0000000 | (0x00000055 << shift); + + nv50_dac_power_wait(device, doff); + nvkm_mask(device, 0x61a004 + doff, field, state); + nv50_dac_power_wait(device, doff); +} + +static void +nv50_dac_state(struct nvkm_ior *dac, struct nvkm_ior_state *state) +{ + struct nvkm_device *device = dac->disp->engine.subdev.device; + const u32 coff = dac->id * 8 + (state == &dac->arm) * 4; + u32 ctrl = nvkm_rd32(device, 0x610b58 + coff); + + state->proto_evo = (ctrl & 0x00000f00) >> 8; + switch (state->proto_evo) { + case 0: state->proto = CRT; break; + default: + state->proto = UNKNOWN; + break; + } - args->v0.load = (loadval & 0x38000000) >> 27; - return 0; + state->head = ctrl & 0x00000003; } -static const struct nvkm_output_func -nv50_dac_output_func = { +static const struct nvkm_ior_func +nv50_dac = { + .state = nv50_dac_state, + .power = nv50_dac_power, + .sense = nv50_dac_sense, + .clock = nv50_dac_clock, }; int -nv50_dac_output_new(struct nvkm_disp *disp, int index, - struct dcb_output *dcbE, struct nvkm_output **poutp) +nv50_dac_new(struct nvkm_disp *disp, int id) { - return nvkm_output_new_(&nv50_dac_output_func, disp, - index, dcbE, poutp); + struct nvkm_device *device = disp->engine.subdev.device; + if (!(nvkm_rd32(device, 0x610184) & (0x00100000 << id))) + return 0; + return nvkm_ior_new_(&nv50_dac, disp, DAC, id); } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c new file mode 100644 index 000000000000..7c5bed29ffef --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c @@ -0,0 +1,652 @@ +/* + * Copyright 2014 Red Hat Inc. + * + * 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 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 HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Ben Skeggs + */ +#include "dp.h" +#include "conn.h" +#include "head.h" +#include "ior.h" + +#include <subdev/bios.h> +#include <subdev/bios/init.h> +#include <subdev/i2c.h> + +#include <nvif/event.h> + +struct lt_state { + struct nvkm_dp *dp; + u8 stat[6]; + u8 conf[4]; + bool pc2; + u8 pc2stat; + u8 pc2conf[2]; +}; + +static int +nvkm_dp_train_sense(struct lt_state *lt, bool pc, u32 delay) +{ + struct nvkm_dp *dp = lt->dp; + int ret; + + if (dp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL]) + mdelay(dp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4); + else + udelay(delay); + + ret = nvkm_rdaux(dp->aux, DPCD_LS02, lt->stat, 6); + if (ret) + return ret; + + if (pc) { + ret = nvkm_rdaux(dp->aux, DPCD_LS0C, <->pc2stat, 1); + if (ret) + lt->pc2stat = 0x00; + OUTP_TRACE(&dp->outp, "status %6ph pc2 %02x", + lt->stat, lt->pc2stat); + } else { + OUTP_TRACE(&dp->outp, "status %6ph", lt->stat); + } + + return 0; +} + +static int +nvkm_dp_train_drive(struct lt_state *lt, bool pc) +{ + struct nvkm_dp *dp = lt->dp; + struct nvkm_ior *ior = dp->outp.ior; + struct nvkm_bios *bios = ior->disp->engine.subdev.device->bios; + struct nvbios_dpout info; + struct nvbios_dpcfg ocfg; + u8 ver, hdr, cnt, len; + u32 data; + int ret, i; + + for (i = 0; i < ior->dp.nr; i++) { + u8 lane = (lt->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; + u8 lpc2 = (lt->pc2stat >> (i * 2)) & 0x3; + u8 lpre = (lane & 0x0c) >> 2; + u8 lvsw = (lane & 0x03) >> 0; + u8 hivs = 3 - lpre; + u8 hipe = 3; + u8 hipc = 3; + + if (lpc2 >= hipc) + lpc2 = hipc | DPCD_LC0F_LANE0_MAX_POST_CURSOR2_REACHED; + if (lpre >= hipe) { + lpre = hipe | DPCD_LC03_MAX_SWING_REACHED; /* yes. */ + lvsw = hivs = 3 - (lpre & 3); + } else + if (lvsw >= hivs) { + lvsw = hivs | DPCD_LC03_MAX_SWING_REACHED; + } + + lt->conf[i] = (lpre << 3) | lvsw; + lt->pc2conf[i >> 1] |= lpc2 << ((i & 1) * 4); + + OUTP_TRACE(&dp->outp, "config lane %d %02x %02x", + i, lt->conf[i], lpc2); + + data = nvbios_dpout_match(bios, dp->outp.info.hasht, + dp->outp.info.hashm, + &ver, &hdr, &cnt, &len, &info); + if (!data) + continue; + + data = nvbios_dpcfg_match(bios, data, lpc2 & 3, lvsw & 3, + lpre & 3, &ver, &hdr, &cnt, &len, + &ocfg); + if (!data) + continue; + + ior->func->dp.drive(ior, i, ocfg.pc, ocfg.dc, + ocfg.pe, ocfg.tx_pu); + } + + ret = nvkm_wraux(dp->aux, DPCD_LC03(0), lt->conf, 4); + if (ret) + return ret; + + if (pc) { + ret = nvkm_wraux(dp->aux, DPCD_LC0F, lt->pc2conf, 2); + if (ret) + return ret; + } + + return 0; +} + +static void +nvkm_dp_train_pattern(struct lt_state *lt, u8 pattern) +{ + struct nvkm_dp *dp = lt->dp; + u8 sink_tp; + + OUTP_TRACE(&dp->outp, "training pattern %d", pattern); + dp->outp.ior->func->dp.pattern(dp->outp.ior, pattern); + + nvkm_rdaux(dp->aux, DPCD_LC02, &sink_tp, 1); + sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET; + sink_tp |= pattern; + nvkm_wraux(dp->aux, DPCD_LC02, &sink_tp, 1); +} + +static int +nvkm_dp_train_eq(struct lt_state *lt) +{ + bool eq_done = false, cr_done = true; + int tries = 0, i; + + if (lt->dp->dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED) + nvkm_dp_train_pattern(lt, 3); + else + nvkm_dp_train_pattern(lt, 2); + + do { + if ((tries && + nvkm_dp_train_drive(lt, lt->pc2)) || + nvkm_dp_train_sense(lt, lt->pc2, 400)) + break; + + eq_done = !!(lt->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE); + for (i = 0; i < lt->dp->outp.ior->dp.nr && eq_done; i++) { + u8 lane = (lt->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; + if (!(lane & DPCD_LS02_LANE0_CR_DONE)) + cr_done = false; + if (!(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) || + !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) + eq_done = false; + } + } while (!eq_done && cr_done && ++tries <= 5); + + return eq_done ? 0 : -1; +} + +static int +nvkm_dp_train_cr(struct lt_state *lt) +{ + bool cr_done = false, abort = false; + int voltage = lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET; + int tries = 0, i; + + nvkm_dp_train_pattern(lt, 1); + + do { + if (nvkm_dp_train_drive(lt, false) || + nvkm_dp_train_sense(lt, false, 100)) + break; + + cr_done = true; + for (i = 0; i < lt->dp->outp.ior->dp.nr; i++) { + u8 lane = (lt->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; + if (!(lane & DPCD_LS02_LANE0_CR_DONE)) { + cr_done = false; + if (lt->conf[i] & DPCD_LC03_MAX_SWING_REACHED) + abort = true; + break; + } + } + + if ((lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET) != voltage) { + voltage = lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET; + tries = 0; + } + } while (!cr_done && !abort && ++tries < 5); + + return cr_done ? 0 : -1; +} + +static int +nvkm_dp_train_links(struct nvkm_dp *dp) +{ + struct nvkm_ior *ior = dp->outp.ior; + struct nvkm_disp *disp = dp->outp.disp; + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_bios *bios = subdev->device->bios; + struct lt_state lt = { + .dp = dp, + }; + u32 lnkcmp; + u8 sink[2]; + int ret; + + OUTP_DBG(&dp->outp, "training %d x %d MB/s", + ior->dp.nr, ior->dp.bw * 27); + + /* Intersect misc. capabilities of the OR and sink. */ + if (disp->engine.subdev.device->chipset < 0xd0) + dp->dpcd[DPCD_RC02] &= ~DPCD_RC02_TPS3_SUPPORTED; + lt.pc2 = dp->dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED; + + /* Set desired link configuration on the source. */ + if ((lnkcmp = lt.dp->info.lnkcmp)) { + if (dp->version < 0x30) { + while ((ior->dp.bw * 2700) < nvbios_rd16(bios, lnkcmp)) + lnkcmp += 4; + lnkcmp = nvbios_rd16(bios, lnkcmp + 2); + } else { + while (ior->dp.bw < nvbios_rd08(bios, lnkcmp)) + lnkcmp += 3; + lnkcmp = nvbios_rd16(bios, lnkcmp + 1); + } + + nvbios_init(subdev, lnkcmp, + init.outp = &dp->outp.info; + init.or = ior->id; + init.link = ior->asy.link; + ); + } + + ret = ior->func->dp.links(ior, dp->aux); + if (ret) { + if (ret < 0) { + OUTP_ERR(&dp->outp, "train failed with %d", ret); + return ret; + } + return 0; + } + + ior->func->dp.power(ior, ior->dp.nr); + + /* Set desired link configuration on the sink. */ + sink[0] = ior->dp.bw; + sink[1] = ior->dp.nr; + if (ior->dp.ef) + sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN; + + ret = nvkm_wraux(dp->aux, DPCD_LC00_LINK_BW_SET, sink, 2); + if (ret) + return ret; + + /* Attempt to train the link in this configuration. */ + memset(lt.stat, 0x00, sizeof(lt.stat)); + ret = nvkm_dp_train_cr(<); + if (ret == 0) + ret = nvkm_dp_train_eq(<); + nvkm_dp_train_pattern(<, 0); + return ret; +} + +static void +nvkm_dp_train_fini(struct nvkm_dp *dp) +{ + /* Execute AfterLinkTraining script from DP Info table. */ + nvbios_init(&dp->outp.disp->engine.subdev, dp->info.script[1], + init.outp = &dp->outp.info; + init.or = dp->outp.ior->id; + init.link = dp->outp.ior->asy.link; + ); +} + +static void +nvkm_dp_train_init(struct nvkm_dp *dp) +{ + /* Execute EnableSpread/DisableSpread script from DP Info table. */ + if (dp->dpcd[DPCD_RC03] & DPCD_RC03_MAX_DOWNSPREAD) { + nvbios_init(&dp->outp.disp->engine.subdev, dp->info.script[2], + init.outp = &dp->outp.info; + init.or = dp->outp.ior->id; + init.link = dp->outp.ior->asy.link; + ); + } else { + nvbios_init(&dp->outp.disp->engine.subdev, dp->info.script[3], + init.outp = &dp->outp.info; + init.or = dp->outp.ior->id; + init.link = dp->outp.ior->asy.link; + ); + } + + /* Execute BeforeLinkTraining script from DP Info table. */ + nvbios_init(&dp->outp.disp->engine.subdev, dp->info.script[0], + init.outp = &dp->outp.info; + init.or = dp->outp.ior->id; + init.link = dp->outp.ior->asy.link; + ); +} + +static const struct dp_rates { + u32 rate; + u8 bw; + u8 nr; +} nvkm_dp_rates[] = { + { 2160000, 0x14, 4 }, + { 1080000, 0x0a, 4 }, + { 1080000, 0x14, 2 }, + { 648000, 0x06, 4 }, + { 540000, 0x0a, 2 }, + { 540000, 0x14, 1 }, + { 324000, 0x06, 2 }, + { 270000, 0x0a, 1 }, + { 162000, 0x06, 1 }, + {} +}; + +static int +nvkm_dp_train(struct nvkm_dp *dp, u32 dataKBps) +{ + struct nvkm_ior *ior = dp->outp.ior; + const u8 sink_nr = dp->dpcd[DPCD_RC02] & DPCD_RC02_MAX_LANE_COUNT; + const u8 sink_bw = dp->dpcd[DPCD_RC01_MAX_LINK_RATE]; + const u8 outp_nr = dp->outp.info.dpconf.link_nr; + const u8 outp_bw = dp->outp.info.dpconf.link_bw; + const struct dp_rates *failsafe = NULL, *cfg; + int ret = -EINVAL; + u8 pwr; + + /* Find the lowest configuration of the OR that can support + * the required link rate. + * + * We will refuse to program the OR to lower rates, even if + * link training fails at higher rates (or even if the sink + * can't support the rate at all, though the DD is supposed + * to prevent such situations from happening). + * + * Attempting to do so can cause the entire display to hang, + * and it's better to have a failed modeset than that. + */ + for (cfg = nvkm_dp_rates; cfg->rate; cfg++) { + if (cfg->nr <= outp_nr && cfg->nr <= outp_bw) + failsafe = cfg; + if (failsafe && cfg[1].rate < dataKBps) + break; + } + + if (WARN_ON(!failsafe)) + return ret; + + /* Ensure sink is not in a low-power state. */ + if (!nvkm_rdaux(dp->aux, DPCD_SC00, &pwr, 1)) { + if ((pwr & DPCD_SC00_SET_POWER) != DPCD_SC00_SET_POWER_D0) { + pwr &= ~DPCD_SC00_SET_POWER; + pwr |= DPCD_SC00_SET_POWER_D0; + nvkm_wraux(dp->aux, DPCD_SC00, &pwr, 1); + } + } + + /* Link training. */ + OUTP_DBG(&dp->outp, "training (min: %d x %d MB/s)", + failsafe->nr, failsafe->bw * 27); + nvkm_dp_train_init(dp); + for (cfg = nvkm_dp_rates; ret < 0 && cfg <= failsafe; cfg++) { + /* Skip configurations not supported by both OR and sink. */ + if ((cfg->nr > outp_nr || cfg->bw > outp_bw || + cfg->nr > sink_nr || cfg->bw > sink_bw)) { + if (cfg != failsafe) + continue; + OUTP_ERR(&dp->outp, "link rate unsupported by sink"); + } + ior->dp.mst = dp->lt.mst; + ior->dp.ef = dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP; + ior->dp.bw = cfg->bw; + ior->dp.nr = cfg->nr; + + /* Program selected link configuration. */ + ret = nvkm_dp_train_links(dp); + } + nvkm_dp_train_fini(dp); + if (ret < 0) + OUTP_ERR(&dp->outp, "training failed"); + else + OUTP_DBG(&dp->outp, "training done"); + atomic_set(&dp->lt.done, 1); + return ret; +} + +static void +nvkm_dp_release(struct nvkm_outp *outp, struct nvkm_ior *ior) +{ + struct nvkm_dp *dp = nvkm_dp(outp); + + /* Prevent link from being retrained if sink sends an IRQ. */ + atomic_set(&dp->lt.done, 0); + ior->dp.nr = 0; + + /* Execute DisableLT script from DP Info Table. */ + nvbios_init(&ior->disp->engine.subdev, dp->info.script[4], + init.outp = &dp->outp.info; + init.or = ior->id; + init.link = ior->arm.link; + ); +} + +static int +nvkm_dp_acquire(struct nvkm_outp *outp) +{ + struct nvkm_dp *dp = nvkm_dp(outp); + struct nvkm_ior *ior = dp->outp.ior; + struct nvkm_head *head; + bool retrain = true; + u32 datakbps = 0; + u32 dataKBps; + u32 linkKBps; + u8 stat[3]; + int ret, i; + + mutex_lock(&dp->mutex); + + /* Check that link configuration meets current requirements. */ + list_for_each_entry(head, &outp->disp->head, head) { + if (ior->asy.head & (1 << head->id)) { + u32 khz = (head->asy.hz >> ior->asy.rgdiv) / 1000; + datakbps += khz * head->asy.or.depth; + } + } + + linkKBps = ior->dp.bw * 27000 * ior->dp.nr; + dataKBps = DIV_ROUND_UP(datakbps, 8); + OUTP_DBG(&dp->outp, "data %d KB/s link %d KB/s mst %d->%d", + dataKBps, linkKBps, ior->dp.mst, dp->lt.mst); + if (linkKBps < dataKBps || ior->dp.mst != dp->lt.mst) { + OUTP_DBG(&dp->outp, "link requirements changed"); + goto done; + } + + /* Check that link is still trained. */ + ret = nvkm_rdaux(dp->aux, DPCD_LS02, stat, 3); + if (ret) { + OUTP_DBG(&dp->outp, + "failed to read link status, assuming no sink"); + goto done; + } + + if (stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE) { + for (i = 0; i < ior->dp.nr; i++) { + u8 lane = (stat[i >> 1] >> ((i & 1) * 4)) & 0x0f; + if (!(lane & DPCD_LS02_LANE0_CR_DONE) || + !(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) || + !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) { + OUTP_DBG(&dp->outp, + "lane %d not equalised", lane); + goto done; + } + } + retrain = false; + } else { + OUTP_DBG(&dp->outp, "no inter-lane alignment"); + } + +done: + if (retrain || !atomic_read(&dp->lt.done)) + ret = nvkm_dp_train(dp, dataKBps); + mutex_unlock(&dp->mutex); + return ret; +} + +static void +nvkm_dp_enable(struct nvkm_dp *dp, bool enable) +{ + struct nvkm_i2c_aux *aux = dp->aux; + + if (enable) { + if (!dp->present) { + OUTP_DBG(&dp->outp, "aux power -> always"); + nvkm_i2c_aux_monitor(aux, true); + dp->present = true; + } + + if (!nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, dp->dpcd, + sizeof(dp->dpcd))) + return; + } + + if (dp->present) { + OUTP_DBG(&dp->outp, "aux power -> demand"); + nvkm_i2c_aux_monitor(aux, false); + dp->present = false; + } + + atomic_set(&dp->lt.done, 0); +} + +static int +nvkm_dp_hpd(struct nvkm_notify *notify) +{ + const struct nvkm_i2c_ntfy_rep *line = notify->data; + struct nvkm_dp *dp = container_of(notify, typeof(*dp), hpd); + struct nvkm_conn *conn = dp->outp.conn; + struct nvkm_disp *disp = dp->outp.disp; + struct nvif_notify_conn_rep_v0 rep = {}; + + OUTP_DBG(&dp->outp, "HPD: %d", line->mask); + if (line->mask & NVKM_I2C_IRQ) { + if (atomic_read(&dp->lt.done)) + dp->outp.func->acquire(&dp->outp); + rep.mask |= NVIF_NOTIFY_CONN_V0_IRQ; + } else { + nvkm_dp_enable(dp, true); + } + + if (line->mask & NVKM_I2C_UNPLUG) + rep.mask |= NVIF_NOTIFY_CONN_V0_UNPLUG; + if (line->mask & NVKM_I2C_PLUG) + rep.mask |= NVIF_NOTIFY_CONN_V0_PLUG; + + nvkm_event_send(&disp->hpd, rep.mask, conn->index, &rep, sizeof(rep)); + return NVKM_NOTIFY_KEEP; +} + +static void +nvkm_dp_fini(struct nvkm_outp *outp) +{ + struct nvkm_dp *dp = nvkm_dp(outp); + nvkm_notify_put(&dp->hpd); + nvkm_dp_enable(dp, false); +} + +static void +nvkm_dp_init(struct nvkm_outp *outp) +{ + struct nvkm_dp *dp = nvkm_dp(outp); + nvkm_notify_put(&dp->outp.conn->hpd); + nvkm_dp_enable(dp, true); + nvkm_notify_get(&dp->hpd); +} + +static void * +nvkm_dp_dtor(struct nvkm_outp *outp) +{ + struct nvkm_dp *dp = nvkm_dp(outp); + nvkm_notify_fini(&dp->hpd); + return dp; +} + +static const struct nvkm_outp_func +nvkm_dp_func = { + .dtor = nvkm_dp_dtor, + .init = nvkm_dp_init, + .fini = nvkm_dp_fini, + .acquire = nvkm_dp_acquire, + .release = nvkm_dp_release, +}; + +static int +nvkm_dp_ctor(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, + struct nvkm_i2c_aux *aux, struct nvkm_dp *dp) +{ + struct nvkm_device *device = disp->engine.subdev.device; + struct nvkm_bios *bios = device->bios; + struct nvkm_i2c *i2c = device->i2c; + u8 hdr, cnt, len; + u32 data; + int ret; + + ret = nvkm_outp_ctor(&nvkm_dp_func, disp, index, dcbE, &dp->outp); + if (ret) + return ret; + + dp->aux = aux; + if (!dp->aux) { + OUTP_ERR(&dp->outp, "no aux"); + return -EINVAL; + } + + /* bios data is not optional */ + data = nvbios_dpout_match(bios, dp->outp.info.hasht, + dp->outp.info.hashm, &dp->version, + &hdr, &cnt, &len, &dp->info); + if (!data) { + OUTP_ERR(&dp->outp, "no bios dp data"); + return -EINVAL; + } + + OUTP_DBG(&dp->outp, "bios dp %02x %02x %02x %02x", + dp->version, hdr, cnt, len); + + /* hotplug detect, replaces gpio-based mechanism with aux events */ + ret = nvkm_notify_init(NULL, &i2c->event, nvkm_dp_hpd, true, + &(struct nvkm_i2c_ntfy_req) { + .mask = NVKM_I2C_PLUG | NVKM_I2C_UNPLUG | + NVKM_I2C_IRQ, + .port = dp->aux->id, + }, + sizeof(struct nvkm_i2c_ntfy_req), + sizeof(struct nvkm_i2c_ntfy_rep), + &dp->hpd); + if (ret) { + OUTP_ERR(&dp->outp, "error monitoring aux hpd: %d", ret); + return ret; + } + + mutex_init(&dp->mutex); + atomic_set(&dp->lt.done, 0); + return 0; +} + +int +nvkm_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, + struct nvkm_outp **poutp) +{ + struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c; + struct nvkm_i2c_aux *aux; + struct nvkm_dp *dp; + + if (dcbE->location == 0) + aux = nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_CCB(dcbE->i2c_index)); + else + aux = nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_EXT(dcbE->extdev)); + + if (!(dp = kzalloc(sizeof(*dp), GFP_KERNEL))) + return -ENOMEM; + *poutp = &dp->outp; + + return nvkm_dp_ctor(disp, index, dcbE, aux, dp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h index baf1dd9ff975..59173c290525 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h @@ -1,6 +1,36 @@ -#ifndef __NVKM_DISP_DPORT_H__ -#define __NVKM_DISP_DPORT_H__ -struct nvkm_output_dp; +#ifndef __NVKM_DISP_DP_H__ +#define __NVKM_DISP_DP_H__ +#define nvkm_dp(p) container_of((p), struct nvkm_dp, outp) +#include "outp.h" + +#include <core/notify.h> +#include <subdev/bios.h> +#include <subdev/bios/dp.h> + +struct nvkm_dp { + union { + struct nvkm_outp base; + struct nvkm_outp outp; + }; + + struct nvbios_dpout info; + u8 version; + + struct nvkm_i2c_aux *aux; + + struct nvkm_notify hpd; + bool present; + u8 dpcd[16]; + + struct mutex mutex; + struct { + atomic_t done; + bool mst; + } lt; +}; + +int nvkm_dp_new(struct nvkm_disp *, int index, struct dcb_output *, + struct nvkm_outp **); /* DPCD Receiver Capabilities */ #define DPCD_RC00_DPCD_REV 0x00000 @@ -76,6 +106,4 @@ struct nvkm_output_dp; #define DPCD_SC00_SET_POWER 0x03 #define DPCD_SC00_SET_POWER_D0 0x01 #define DPCD_SC00_SET_POWER_D3 0x03 - -void nvkm_dp_train(struct nvkm_output_dp *); #endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.c deleted file mode 100644 index 4a93ceb850ac..000000000000 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.c +++ /dev/null @@ -1,401 +0,0 @@ -/* - * Copyright 2013 Red Hat Inc. - * - * 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 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 HOLDER(S) OR AUTHOR(S) 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. - * - * Authors: Ben Skeggs - */ -#include "dport.h" -#include "outpdp.h" -#include "nv50.h" - -#include <subdev/bios.h> -#include <subdev/bios/init.h> -#include <subdev/i2c.h> - -#include <nvif/class.h> - -/****************************************************************************** - * link training - *****************************************************************************/ -struct dp_state { - struct nvkm_output_dp *outp; - int link_nr; - u32 link_bw; - u8 stat[6]; - u8 conf[4]; - bool pc2; - u8 pc2stat; - u8 pc2conf[2]; -}; - -static int -dp_set_link_config(struct dp_state *dp) -{ - struct nvkm_output_dp *outp = dp->outp; - struct nvkm_disp *disp = outp->base.disp; - struct nvkm_subdev *subdev = &disp->engine.subdev; - struct nvkm_bios *bios = subdev->device->bios; - struct nvbios_init init = { - .subdev = subdev, - .bios = bios, - .offset = 0x0000, - .outp = &outp->base.info, - .crtc = -1, - .execute = 1, - }; - u32 lnkcmp; - u8 sink[2]; - int ret; - - OUTP_DBG(&outp->base, "%d lanes at %d KB/s", dp->link_nr, dp->link_bw); - - /* set desired link configuration on the source */ - if ((lnkcmp = dp->outp->info.lnkcmp)) { - if (outp->version < 0x30) { - while ((dp->link_bw / 10) < nvbios_rd16(bios, lnkcmp)) - lnkcmp += 4; - init.offset = nvbios_rd16(bios, lnkcmp + 2); - } else { - while ((dp->link_bw / 27000) < nvbios_rd08(bios, lnkcmp)) - lnkcmp += 3; - init.offset = nvbios_rd16(bios, lnkcmp + 1); - } - - nvbios_exec(&init); - } - - ret = outp->func->lnk_ctl(outp, dp->link_nr, dp->link_bw / 27000, - outp->dpcd[DPCD_RC02] & - DPCD_RC02_ENHANCED_FRAME_CAP); - if (ret) { - if (ret < 0) - OUTP_ERR(&outp->base, "lnk_ctl failed with %d", ret); - return ret; - } - - outp->func->lnk_pwr(outp, dp->link_nr); - - /* set desired link configuration on the sink */ - sink[0] = dp->link_bw / 27000; - sink[1] = dp->link_nr; - if (outp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP) - sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN; - - return nvkm_wraux(outp->aux, DPCD_LC00_LINK_BW_SET, sink, 2); -} - -static void -dp_set_training_pattern(struct dp_state *dp, u8 pattern) -{ - struct nvkm_output_dp *outp = dp->outp; - u8 sink_tp; - - OUTP_DBG(&outp->base, "training pattern %d", pattern); - outp->func->pattern(outp, pattern); - - nvkm_rdaux(outp->aux, DPCD_LC02, &sink_tp, 1); - sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET; - sink_tp |= pattern; - nvkm_wraux(outp->aux, DPCD_LC02, &sink_tp, 1); -} - -static int -dp_link_train_commit(struct dp_state *dp, bool pc) -{ - struct nvkm_output_dp *outp = dp->outp; - int ret, i; - - for (i = 0; i < dp->link_nr; i++) { - u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; - u8 lpc2 = (dp->pc2stat >> (i * 2)) & 0x3; - u8 lpre = (lane & 0x0c) >> 2; - u8 lvsw = (lane & 0x03) >> 0; - u8 hivs = 3 - lpre; - u8 hipe = 3; - u8 hipc = 3; - - if (lpc2 >= hipc) - lpc2 = hipc | DPCD_LC0F_LANE0_MAX_POST_CURSOR2_REACHED; - if (lpre >= hipe) { - lpre = hipe | DPCD_LC03_MAX_SWING_REACHED; /* yes. */ - lvsw = hivs = 3 - (lpre & 3); - } else - if (lvsw >= hivs) { - lvsw = hivs | DPCD_LC03_MAX_SWING_REACHED; - } - - dp->conf[i] = (lpre << 3) | lvsw; - dp->pc2conf[i >> 1] |= lpc2 << ((i & 1) * 4); - - OUTP_DBG(&outp->base, "config lane %d %02x %02x", - i, dp->conf[i], lpc2); - outp->func->drv_ctl(outp, i, lvsw & 3, lpre & 3, lpc2 & 3); - } - - ret = nvkm_wraux(outp->aux, DPCD_LC03(0), dp->conf, 4); - if (ret) - return ret; - - if (pc) { - ret = nvkm_wraux(outp->aux, DPCD_LC0F, dp->pc2conf, 2); - if (ret) - return ret; - } - - return 0; -} - -static int -dp_link_train_update(struct dp_state *dp, bool pc, u32 delay) -{ - struct nvkm_output_dp *outp = dp->outp; - int ret; - - if (outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL]) - mdelay(outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4); - else - udelay(delay); - - ret = nvkm_rdaux(outp->aux, DPCD_LS02, dp->stat, 6); - if (ret) - return ret; - - if (pc) { - ret = nvkm_rdaux(outp->aux, DPCD_LS0C, &dp->pc2stat, 1); - if (ret) - dp->pc2stat = 0x00; - OUTP_DBG(&outp->base, "status %6ph pc2 %02x", - dp->stat, dp->pc2stat); - } else { - OUTP_DBG(&outp->base, "status %6ph", dp->stat); - } - - return 0; -} - -static int -dp_link_train_cr(struct dp_state *dp) -{ - bool cr_done = false, abort = false; - int voltage = dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET; - int tries = 0, i; - - dp_set_training_pattern(dp, 1); - - do { - if (dp_link_train_commit(dp, false) || - dp_link_train_update(dp, false, 100)) - break; - - cr_done = true; - for (i = 0; i < dp->link_nr; i++) { - u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; - if (!(lane & DPCD_LS02_LANE0_CR_DONE)) { - cr_done = false; - if (dp->conf[i] & DPCD_LC03_MAX_SWING_REACHED) - abort = true; - break; - } - } - - if ((dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET) != voltage) { - voltage = dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET; - tries = 0; - } - } while (!cr_done && !abort && ++tries < 5); - - return cr_done ? 0 : -1; -} - -static int -dp_link_train_eq(struct dp_state *dp) -{ - struct nvkm_output_dp *outp = dp->outp; - bool eq_done = false, cr_done = true; - int tries = 0, i; - - if (outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED) - dp_set_training_pattern(dp, 3); - else - dp_set_training_pattern(dp, 2); - - do { - if ((tries && - dp_link_train_commit(dp, dp->pc2)) || - dp_link_train_update(dp, dp->pc2, 400)) - break; - - eq_done = !!(dp->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE); - for (i = 0; i < dp->link_nr && eq_done; i++) { - u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; - if (!(lane & DPCD_LS02_LANE0_CR_DONE)) - cr_done = false; - if (!(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) || - !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) - eq_done = false; - } - } while (!eq_done && cr_done && ++tries <= 5); - - return eq_done ? 0 : -1; -} - -static void -dp_link_train_init(struct dp_state *dp, bool spread) -{ - struct nvkm_output_dp *outp = dp->outp; - struct nvkm_disp *disp = outp->base.disp; - struct nvkm_subdev *subdev = &disp->engine.subdev; - struct nvbios_init init = { - .subdev = subdev, - .bios = subdev->device->bios, - .outp = &outp->base.info, - .crtc = -1, - .execute = 1, - }; - - /* set desired spread */ - if (spread) - init.offset = outp->info.script[2]; - else - init.offset = outp->info.script[3]; - nvbios_exec(&init); - - /* pre-train script */ - init.offset = outp->info.script[0]; - nvbios_exec(&init); -} - -static void -dp_link_train_fini(struct dp_state *dp) -{ - struct nvkm_output_dp *outp = dp->outp; - struct nvkm_disp *disp = outp->base.disp; - struct nvkm_subdev *subdev = &disp->engine.subdev; - struct nvbios_init init = { - .subdev = subdev, - .bios = subdev->device->bios, - .outp = &outp->base.info, - .crtc = -1, - .execute = 1, - }; - - /* post-train script */ - init.offset = outp->info.script[1], - nvbios_exec(&init); -} - -static const struct dp_rates { - u32 rate; - u8 bw; - u8 nr; -} nvkm_dp_rates[] = { - { 2160000, 0x14, 4 }, - { 1080000, 0x0a, 4 }, - { 1080000, 0x14, 2 }, - { 648000, 0x06, 4 }, - { 540000, 0x0a, 2 }, - { 540000, 0x14, 1 }, - { 324000, 0x06, 2 }, - { 270000, 0x0a, 1 }, - { 162000, 0x06, 1 }, - {} -}; - -void -nvkm_dp_train(struct nvkm_output_dp *outp) -{ - struct nv50_disp *disp = nv50_disp(outp->base.disp); - const struct dp_rates *cfg = nvkm_dp_rates; - struct dp_state _dp = { - .outp = outp, - }, *dp = &_dp; - u32 datarate = 0; - u8 pwr; - int ret; - - if (!outp->base.info.location && disp->func->sor.magic) - disp->func->sor.magic(&outp->base); - - /* bring capabilities within encoder limits */ - if (disp->base.engine.subdev.device->chipset < 0xd0) - outp->dpcd[2] &= ~DPCD_RC02_TPS3_SUPPORTED; - if ((outp->dpcd[2] & 0x1f) > outp->base.info.dpconf.link_nr) { - outp->dpcd[2] &= ~DPCD_RC02_MAX_LANE_COUNT; - outp->dpcd[2] |= outp->base.info.dpconf.link_nr; - } - if (outp->dpcd[1] > outp->base.info.dpconf.link_bw) - outp->dpcd[1] = outp->base.info.dpconf.link_bw; - dp->pc2 = outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED; - - /* restrict link config to the lowest required rate, if requested */ - if (datarate) { - datarate = (datarate / 8) * 10; /* 8B/10B coding overhead */ - while (cfg[1].rate >= datarate) - cfg++; - } - cfg--; - - /* ensure sink is not in a low-power state */ - if (!nvkm_rdaux(outp->aux, DPCD_SC00, &pwr, 1)) { - if ((pwr & DPCD_SC00_SET_POWER) != DPCD_SC00_SET_POWER_D0) { - pwr &= ~DPCD_SC00_SET_POWER; - pwr |= DPCD_SC00_SET_POWER_D0; - nvkm_wraux(outp->aux, DPCD_SC00, &pwr, 1); - } - } - - /* enable down-spreading and execute pre-train script from vbios */ - dp_link_train_init(dp, outp->dpcd[3] & 0x01); - - while (ret = -EIO, (++cfg)->rate) { - /* select next configuration supported by encoder and sink */ - while (cfg->nr > (outp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT) || - cfg->bw > (outp->dpcd[DPCD_RC01_MAX_LINK_RATE])) - cfg++; - dp->link_bw = cfg->bw * 27000; - dp->link_nr = cfg->nr; - - /* program selected link configuration */ - ret = dp_set_link_config(dp); - if (ret == 0) { - /* attempt to train the link at this configuration */ - memset(dp->stat, 0x00, sizeof(dp->stat)); - if (!dp_link_train_cr(dp) && - !dp_link_train_eq(dp)) - break; - } else - if (ret) { - /* dp_set_link_config() handled training, or - * we failed to communicate with the sink. - */ - break; - } - } - - /* finish link training and execute post-train script from vbios */ - dp_set_training_pattern(dp, 0); - if (ret < 0) - OUTP_ERR(&outp->base, "link training failed"); - - dp_link_train_fini(dp); - - OUTP_DBG(&outp->base, "training complete"); - atomic_set(&outp->lt.done, 1); -} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c index 3e3e592cd09f..842e1b72ee42 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c @@ -22,30 +22,20 @@ * Authors: Ben Skeggs */ #include "nv50.h" +#include "head.h" +#include "ior.h" #include "rootnv50.h" static const struct nv50_disp_func g84_disp = { .intr = nv50_disp_intr, .uevent = &nv50_disp_chan_uevent, - .super = nv50_disp_intr_supervisor, + .super = nv50_disp_super, .root = &g84_disp_root_oclass, - .head.vblank_init = nv50_disp_vblank_init, - .head.vblank_fini = nv50_disp_vblank_fini, - .head.scanoutpos = nv50_disp_root_scanoutpos, - .outp.internal.crt = nv50_dac_output_new, - .outp.internal.tmds = nv50_sor_output_new, - .outp.internal.lvds = nv50_sor_output_new, - .outp.external.tmds = nv50_pior_output_new, - .outp.external.dp = nv50_pior_dp_new, - .dac.nr = 3, - .dac.power = nv50_dac_power, - .dac.sense = nv50_dac_sense, - .sor.nr = 2, - .sor.power = nv50_sor_power, - .sor.hdmi = g84_hdmi_ctrl, - .pior.nr = 3, - .pior.power = nv50_pior_power, + .head.new = nv50_head_new, + .dac = { .nr = 3, .new = nv50_dac_new }, + .sor = { .nr = 2, .new = g84_sor_new }, + .pior = { .nr = 3, .new = nv50_pior_new }, }; int diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c index 7a7af3b478f8..d184e6ab8918 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c @@ -22,31 +22,20 @@ * Authors: Ben Skeggs */ #include "nv50.h" +#include "head.h" +#include "ior.h" #include "rootnv50.h" static const struct nv50_disp_func g94_disp = { .intr = nv50_disp_intr, .uevent = &nv50_disp_chan_uevent, - .super = nv50_disp_intr_supervisor, + .super = nv50_disp_super, .root = &g94_disp_root_oclass, - .head.vblank_init = nv50_disp_vblank_init, - .head.vblank_fini = nv50_disp_vblank_fini, - .head.scanoutpos = nv50_disp_root_scanoutpos, - .outp.internal.crt = nv50_dac_output_new, - .outp.internal.tmds = nv50_sor_output_new, - .outp.internal.lvds = nv50_sor_output_new, - .outp.internal.dp = g94_sor_dp_new, - .outp.external.tmds = nv50_pior_output_new, - .outp.external.dp = nv50_pior_dp_new, - .dac.nr = 3, - .dac.power = nv50_dac_power, - .dac.sense = nv50_dac_sense, - .sor.nr = 4, - .sor.power = nv50_sor_power, - .sor.hdmi = g84_hdmi_ctrl, - .pior.nr = 3, - .pior.power = nv50_pior_power, + .head.new = nv50_head_new, + .dac = { .nr = 3, .new = nv50_dac_new }, + .sor = { .nr = 4, .new = g94_sor_new }, + .pior = { .nr = 3, .new = nv50_pior_new }, }; int diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c index 7b346ccc38b7..d8765b57180b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c @@ -22,397 +22,63 @@ * Authors: Ben Skeggs */ #include "nv50.h" +#include "head.h" +#include "ior.h" #include "rootnv50.h" -#include <subdev/bios.h> -#include <subdev/bios/disp.h> -#include <subdev/bios/init.h> -#include <subdev/bios/pll.h> -#include <subdev/devinit.h> - -void -gf119_disp_vblank_init(struct nv50_disp *disp, int head) -{ - struct nvkm_device *device = disp->base.engine.subdev.device; - nvkm_mask(device, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000001); -} - -void -gf119_disp_vblank_fini(struct nv50_disp *disp, int head) -{ - struct nvkm_device *device = disp->base.engine.subdev.device; - nvkm_mask(device, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000000); -} - -static struct nvkm_output * -exec_lookup(struct nv50_disp *disp, int head, int or, u32 ctrl, - u32 *data, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, - struct nvbios_outp *info) -{ - struct nvkm_subdev *subdev = &disp->base.engine.subdev; - struct nvkm_bios *bios = subdev->device->bios; - struct nvkm_output *outp; - u16 mask, type; - - if (or < 4) { - type = DCB_OUTPUT_ANALOG; - mask = 0; - } else { - or -= 4; - switch (ctrl & 0x00000f00) { - case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break; - case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break; - case 0x00000200: type = DCB_OUTPUT_TMDS; mask = 2; break; - case 0x00000500: type = DCB_OUTPUT_TMDS; mask = 3; break; - case 0x00000800: type = DCB_OUTPUT_DP; mask = 1; break; - case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break; - default: - nvkm_error(subdev, "unknown SOR mc %08x\n", ctrl); - return NULL; - } - } - - mask = 0x00c0 & (mask << 6); - mask |= 0x0001 << or; - mask |= 0x0100 << head; - - list_for_each_entry(outp, &disp->base.outp, head) { - if ((outp->info.hasht & 0xff) == type && - (outp->info.hashm & mask) == mask) { - *data = nvbios_outp_match(bios, outp->info.hasht, mask, - ver, hdr, cnt, len, info); - if (!*data) - return NULL; - return outp; - } - } - - return NULL; -} - -static struct nvkm_output * -exec_script(struct nv50_disp *disp, int head, int id) -{ - struct nvkm_subdev *subdev = &disp->base.engine.subdev; - struct nvkm_device *device = subdev->device; - struct nvkm_bios *bios = device->bios; - struct nvkm_output *outp; - struct nvbios_outp info; - u8 ver, hdr, cnt, len; - u32 data, ctrl = 0; - int or; - - for (or = 0; !(ctrl & (1 << head)) && or < 8; or++) { - ctrl = nvkm_rd32(device, 0x640180 + (or * 0x20)); - if (ctrl & (1 << head)) - break; - } - - if (or == 8) - return NULL; - - outp = exec_lookup(disp, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info); - if (outp) { - struct nvbios_init init = { - .subdev = subdev, - .bios = bios, - .offset = info.script[id], - .outp = &outp->info, - .crtc = head, - .execute = 1, - }; - - nvbios_exec(&init); - } - - return outp; -} - -static struct nvkm_output * -exec_clkcmp(struct nv50_disp *disp, int head, int id, u32 pclk, u32 *conf) -{ - struct nvkm_subdev *subdev = &disp->base.engine.subdev; - struct nvkm_device *device = subdev->device; - struct nvkm_bios *bios = device->bios; - struct nvkm_output *outp; - struct nvbios_outp info1; - struct nvbios_ocfg info2; - u8 ver, hdr, cnt, len; - u32 data, ctrl = 0; - int or; - - for (or = 0; !(ctrl & (1 << head)) && or < 8; or++) { - ctrl = nvkm_rd32(device, 0x660180 + (or * 0x20)); - if (ctrl & (1 << head)) - break; - } - - if (or == 8) - return NULL; - - outp = exec_lookup(disp, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info1); - if (!outp) - return NULL; - - *conf = (ctrl & 0x00000f00) >> 8; - switch (outp->info.type) { - case DCB_OUTPUT_TMDS: - if (*conf == 5) - *conf |= 0x0100; - break; - case DCB_OUTPUT_LVDS: - *conf |= disp->sor.lvdsconf; - break; - default: - break; - } - - data = nvbios_ocfg_match(bios, data, *conf & 0xff, *conf >> 8, - &ver, &hdr, &cnt, &len, &info2); - if (data && id < 0xff) { - data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk); - if (data) { - struct nvbios_init init = { - .subdev = subdev, - .bios = bios, - .offset = data, - .outp = &outp->info, - .crtc = head, - .execute = 1, - }; - - nvbios_exec(&init); - } - } - - return outp; -} - -static void -gf119_disp_intr_unk1_0(struct nv50_disp *disp, int head) -{ - exec_script(disp, head, 1); -} - -static void -gf119_disp_intr_unk2_0(struct nv50_disp *disp, int head) -{ - struct nvkm_subdev *subdev = &disp->base.engine.subdev; - struct nvkm_output *outp = exec_script(disp, head, 2); - - /* see note in nv50_disp_intr_unk20_0() */ - if (outp && outp->info.type == DCB_OUTPUT_DP) { - struct nvkm_output_dp *outpdp = nvkm_output_dp(outp); - if (!outpdp->lt.mst) { - struct nvbios_init init = { - .subdev = subdev, - .bios = subdev->device->bios, - .outp = &outp->info, - .crtc = head, - .offset = outpdp->info.script[4], - .execute = 1, - }; - - nvkm_notify_put(&outpdp->irq); - nvbios_exec(&init); - atomic_set(&outpdp->lt.done, 0); - } - } -} - -static void -gf119_disp_intr_unk2_1(struct nv50_disp *disp, int head) -{ - struct nvkm_device *device = disp->base.engine.subdev.device; - struct nvkm_devinit *devinit = device->devinit; - u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000; - if (pclk) - nvkm_devinit_pll_set(devinit, PLL_VPLL0 + head, pclk); - nvkm_wr32(device, 0x612200 + (head * 0x800), 0x00000000); -} - -static void -gf119_disp_intr_unk2_2_tu(struct nv50_disp *disp, int head, - struct dcb_output *outp) -{ - struct nvkm_device *device = disp->base.engine.subdev.device; - const int or = ffs(outp->or) - 1; - const u32 ctrl = nvkm_rd32(device, 0x660200 + (or * 0x020)); - const u32 conf = nvkm_rd32(device, 0x660404 + (head * 0x300)); - const s32 vactive = nvkm_rd32(device, 0x660414 + (head * 0x300)) & 0xffff; - const s32 vblanke = nvkm_rd32(device, 0x66041c + (head * 0x300)) & 0xffff; - const s32 vblanks = nvkm_rd32(device, 0x660420 + (head * 0x300)) & 0xffff; - const u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000; - const u32 link = ((ctrl & 0xf00) == 0x800) ? 0 : 1; - const u32 hoff = (head * 0x800); - const u32 soff = ( or * 0x800); - const u32 loff = (link * 0x080) + soff; - const u32 symbol = 100000; - const u32 TU = 64; - u32 dpctrl = nvkm_rd32(device, 0x61c10c + loff); - u32 clksor = nvkm_rd32(device, 0x612300 + soff); - u32 datarate, link_nr, link_bw, bits; - u64 ratio, value; - - link_nr = hweight32(dpctrl & 0x000f0000); - link_bw = (clksor & 0x007c0000) >> 18; - link_bw *= 27000; - - /* symbols/hblank - algorithm taken from comments in tegra driver */ - value = vblanke + vactive - vblanks - 7; - value = value * link_bw; - do_div(value, pclk); - value = value - (3 * !!(dpctrl & 0x00004000)) - (12 / link_nr); - nvkm_mask(device, 0x616620 + hoff, 0x0000ffff, value); - - /* symbols/vblank - algorithm taken from comments in tegra driver */ - value = vblanks - vblanke - 25; - value = value * link_bw; - do_div(value, pclk); - value = value - ((36 / link_nr) + 3) - 1; - nvkm_mask(device, 0x616624 + hoff, 0x00ffffff, value); - - /* watermark */ - if ((conf & 0x3c0) == 0x180) bits = 30; - else if ((conf & 0x3c0) == 0x140) bits = 24; - else bits = 18; - datarate = (pclk * bits) / 8; - - ratio = datarate; - ratio *= symbol; - do_div(ratio, link_nr * link_bw); - - value = (symbol - ratio) * TU; - value *= ratio; - do_div(value, symbol); - do_div(value, symbol); - - value += 5; - value |= 0x08000000; - - nvkm_wr32(device, 0x616610 + hoff, value); -} - -static void -gf119_disp_intr_unk2_2(struct nv50_disp *disp, int head) -{ - struct nvkm_device *device = disp->base.engine.subdev.device; - struct nvkm_output *outp; - u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000; - u32 conf, addr, data; - - outp = exec_clkcmp(disp, head, 0xff, pclk, &conf); - if (!outp) - return; - - /* see note in nv50_disp_intr_unk20_2() */ - if (outp->info.type == DCB_OUTPUT_DP) { - u32 sync = nvkm_rd32(device, 0x660404 + (head * 0x300)); - switch ((sync & 0x000003c0) >> 6) { - case 6: pclk = pclk * 30; break; - case 5: pclk = pclk * 24; break; - case 2: - default: - pclk = pclk * 18; - break; - } - - if (nvkm_output_dp_train(outp, pclk)) - OUTP_ERR(outp, "link not trained before attach"); - } else { - if (disp->func->sor.magic) - disp->func->sor.magic(outp); - } - - exec_clkcmp(disp, head, 0, pclk, &conf); - - if (outp->info.type == DCB_OUTPUT_ANALOG) { - addr = 0x612280 + (ffs(outp->info.or) - 1) * 0x800; - data = 0x00000000; - } else { - addr = 0x612300 + (ffs(outp->info.or) - 1) * 0x800; - data = (conf & 0x0100) ? 0x00000101 : 0x00000000; - switch (outp->info.type) { - case DCB_OUTPUT_TMDS: - nvkm_mask(device, addr, 0x007c0000, 0x00280000); - break; - case DCB_OUTPUT_DP: - gf119_disp_intr_unk2_2_tu(disp, head, &outp->info); - break; - default: - break; - } - } - - nvkm_mask(device, addr, 0x00000707, data); -} - -static void -gf119_disp_intr_unk4_0(struct nv50_disp *disp, int head) -{ - struct nvkm_device *device = disp->base.engine.subdev.device; - u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000; - u32 conf; - - exec_clkcmp(disp, head, 1, pclk, &conf); -} - void -gf119_disp_intr_supervisor(struct work_struct *work) +gf119_disp_super(struct work_struct *work) { struct nv50_disp *disp = container_of(work, struct nv50_disp, supervisor); struct nvkm_subdev *subdev = &disp->base.engine.subdev; struct nvkm_device *device = subdev->device; + struct nvkm_head *head; u32 mask[4]; - int head; nvkm_debug(subdev, "supervisor %d\n", ffs(disp->super)); - for (head = 0; head < disp->base.head.nr; head++) { - mask[head] = nvkm_rd32(device, 0x6101d4 + (head * 0x800)); - nvkm_debug(subdev, "head %d: %08x\n", head, mask[head]); + list_for_each_entry(head, &disp->base.head, head) { + mask[head->id] = nvkm_rd32(device, 0x6101d4 + (head->id * 0x800)); + HEAD_DBG(head, "%08x", mask[head->id]); } if (disp->super & 0x00000001) { nv50_disp_chan_mthd(disp->chan[0], NV_DBG_DEBUG); - for (head = 0; head < disp->base.head.nr; head++) { - if (!(mask[head] & 0x00001000)) + nv50_disp_super_1(disp); + list_for_each_entry(head, &disp->base.head, head) { + if (!(mask[head->id] & 0x00001000)) continue; - nvkm_debug(subdev, "supervisor 1.0 - head %d\n", head); - gf119_disp_intr_unk1_0(disp, head); + nv50_disp_super_1_0(disp, head); } } else if (disp->super & 0x00000002) { - for (head = 0; head < disp->base.head.nr; head++) { - if (!(mask[head] & 0x00001000)) + list_for_each_entry(head, &disp->base.head, head) { + if (!(mask[head->id] & 0x00001000)) continue; - nvkm_debug(subdev, "supervisor 2.0 - head %d\n", head); - gf119_disp_intr_unk2_0(disp, head); + nv50_disp_super_2_0(disp, head); } - for (head = 0; head < disp->base.head.nr; head++) { - if (!(mask[head] & 0x00010000)) + nvkm_outp_route(&disp->base); + list_for_each_entry(head, &disp->base.head, head) { + if (!(mask[head->id] & 0x00010000)) continue; - nvkm_debug(subdev, "supervisor 2.1 - head %d\n", head); - gf119_disp_intr_unk2_1(disp, head); + nv50_disp_super_2_1(disp, head); } - for (head = 0; head < disp->base.head.nr; head++) { - if (!(mask[head] & 0x00001000)) + list_for_each_entry(head, &disp->base.head, head) { + if (!(mask[head->id] & 0x00001000)) continue; - nvkm_debug(subdev, "supervisor 2.2 - head %d\n", head); - gf119_disp_intr_unk2_2(disp, head); + nv50_disp_super_2_2(disp, head); } } else if (disp->super & 0x00000004) { - for (head = 0; head < disp->base.head.nr; head++) { - if (!(mask[head] & 0x00001000)) + list_for_each_entry(head, &disp->base.head, head) { + if (!(mask[head->id] & 0x00001000)) continue; - nvkm_debug(subdev, "supervisor 3.0 - head %d\n", head); - gf119_disp_intr_unk4_0(disp, head); + nv50_disp_super_3_0(disp, head); } } - for (head = 0; head < disp->base.head.nr; head++) - nvkm_wr32(device, 0x6101d4 + (head * 0x800), 0x00000000); + list_for_each_entry(head, &disp->base.head, head) + nvkm_wr32(device, 0x6101d4 + (head->id * 0x800), 0x00000000); nvkm_wr32(device, 0x6101d0, 0x80000000); } @@ -447,8 +113,8 @@ gf119_disp_intr(struct nv50_disp *disp) { struct nvkm_subdev *subdev = &disp->base.engine.subdev; struct nvkm_device *device = subdev->device; + struct nvkm_head *head; u32 intr = nvkm_rd32(device, 0x610088); - int i; if (intr & 0x00000001) { u32 stat = nvkm_rd32(device, 0x61008c); @@ -472,7 +138,7 @@ gf119_disp_intr(struct nv50_disp *disp) u32 stat = nvkm_rd32(device, 0x6100ac); if (stat & 0x00000007) { disp->super = (stat & 0x00000007); - schedule_work(&disp->supervisor); + queue_work(disp->wq, &disp->supervisor); nvkm_wr32(device, 0x6100ac, disp->super); stat &= ~0x00000007; } @@ -485,14 +151,15 @@ gf119_disp_intr(struct nv50_disp *disp) intr &= ~0x00100000; } - for (i = 0; i < disp->base.head.nr; i++) { - u32 mask = 0x01000000 << i; + list_for_each_entry(head, &disp->base.head, head) { + const u32 hoff = head->id * 0x800; + u32 mask = 0x01000000 << head->id; if (mask & intr) { - u32 stat = nvkm_rd32(device, 0x6100bc + (i * 0x800)); + u32 stat = nvkm_rd32(device, 0x6100bc + hoff); if (stat & 0x00000001) - nvkm_disp_vblank(&disp->base, i); - nvkm_mask(device, 0x6100bc + (i * 0x800), 0, 0); - nvkm_rd32(device, 0x6100c0 + (i * 0x800)); + nvkm_disp_vblank(&disp->base, head->id); + nvkm_mask(device, 0x6100bc + hoff, 0, 0); + nvkm_rd32(device, 0x6100c0 + hoff); } } } @@ -510,22 +177,11 @@ gf119_disp = { .intr = gf119_disp_intr, .intr_error = gf119_disp_intr_error, .uevent = &gf119_disp_chan_uevent, - .super = gf119_disp_intr_supervisor, + .super = gf119_disp_super, .root = &gf119_disp_root_oclass, - .head.vblank_init = gf119_disp_vblank_init, - .head.vblank_fini = gf119_disp_vblank_fini, - .head.scanoutpos = gf119_disp_root_scanoutpos, - .outp.internal.crt = nv50_dac_output_new, - .outp.internal.tmds = nv50_sor_output_new, - .outp.internal.lvds = nv50_sor_output_new, - .outp.internal.dp = gf119_sor_dp_new, - .dac.nr = 3, - .dac.power = nv50_dac_power, - .dac.sense = nv50_dac_sense, - .sor.nr = 4, - .sor.power = nv50_sor_power, - .sor.hda_eld = gf119_hda_eld, - .sor.hdmi = gf119_hdmi_ctrl, + .head.new = gf119_head_new, + .dac = { .nr = 3, .new = gf119_dac_new }, + .sor = { .nr = 4, .new = gf119_sor_new }, }; int diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c index 37f145cf30d7..e8fe9f315d64 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c @@ -22,6 +22,8 @@ * Authors: Ben Skeggs */ #include "nv50.h" +#include "head.h" +#include "ior.h" #include "rootnv50.h" static const struct nv50_disp_func @@ -29,22 +31,11 @@ gk104_disp = { .intr = gf119_disp_intr, .intr_error = gf119_disp_intr_error, .uevent = &gf119_disp_chan_uevent, - .super = gf119_disp_intr_supervisor, + .super = gf119_disp_super, .root = &gk104_disp_root_oclass, - .head.vblank_init = gf119_disp_vblank_init, - .head.vblank_fini = gf119_disp_vblank_fini, - .head.scanoutpos = gf119_disp_root_scanoutpos, - .outp.internal.crt = nv50_dac_output_new, - .outp.internal.tmds = nv50_sor_output_new, - .outp.internal.lvds = nv50_sor_output_new, - .outp.internal.dp = gf119_sor_dp_new, - .dac.nr = 3, - .dac.power = nv50_dac_power, - .dac.sense = nv50_dac_sense, - .sor.nr = 4, - .sor.power = nv50_sor_power, - .sor.hda_eld = gf119_hda_eld, - .sor.hdmi = gk104_hdmi_ctrl, + .head.new = gf119_head_new, + .dac = { .nr = 3, .new = gf119_dac_new }, + .sor = { .nr = 4, .new = gk104_sor_new }, }; int diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk110.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk110.c index e14ac946608c..769687502e7a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk110.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk110.c @@ -22,6 +22,8 @@ * Authors: Ben Skeggs */ #include "nv50.h" +#include "head.h" +#include "ior.h" #include "rootnv50.h" static const struct nv50_disp_func @@ -29,22 +31,11 @@ gk110_disp = { .intr = gf119_disp_intr, .intr_error = gf119_disp_intr_error, .uevent = &gf119_disp_chan_uevent, - .super = gf119_disp_intr_supervisor, + .super = gf119_disp_super, .root = &gk110_disp_root_oclass, - .head.vblank_init = gf119_disp_vblank_init, - .head.vblank_fini = gf119_disp_vblank_fini, - .head.scanoutpos = gf119_disp_root_scanoutpos, - .outp.internal.crt = nv50_dac_output_new, - .outp.internal.tmds = nv50_sor_output_new, - .outp.internal.lvds = nv50_sor_output_new, - .outp.internal.dp = gf119_sor_dp_new, - .dac.nr = 3, - .dac.power = nv50_dac_power, - .dac.sense = nv50_dac_sense, - .sor.nr = 4, - .sor.power = nv50_sor_power, - .sor.hda_eld = gf119_hda_eld, - .sor.hdmi = gk104_hdmi_ctrl, + .head.new = gf119_head_new, + .dac = { .nr = 3, .new = gf119_dac_new }, + .sor = { .nr = 4, .new = gk104_sor_new }, }; int diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c index 2f2437cc5891..ede70e5d188e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c @@ -22,6 +22,8 @@ * Authors: Ben Skeggs */ #include "nv50.h" +#include "head.h" +#include "ior.h" #include "rootnv50.h" static const struct nv50_disp_func @@ -29,22 +31,11 @@ gm107_disp = { .intr = gf119_disp_intr, .intr_error = gf119_disp_intr_error, .uevent = &gf119_disp_chan_uevent, - .super = gf119_disp_intr_supervisor, + .super = gf119_disp_super, .root = &gm107_disp_root_oclass, - .head.vblank_init = gf119_disp_vblank_init, - .head.vblank_fini = gf119_disp_vblank_fini, - .head.scanoutpos = gf119_disp_root_scanoutpos, - .outp.internal.crt = nv50_dac_output_new, - .outp.internal.tmds = nv50_sor_output_new, - .outp.internal.lvds = nv50_sor_output_new, - .outp.internal.dp = gm107_sor_dp_new, - .dac.nr = 3, - .dac.power = nv50_dac_power, - .dac.sense = nv50_dac_sense, - .sor.nr = 4, - .sor.power = nv50_sor_power, - .sor.hda_eld = gf119_hda_eld, - .sor.hdmi = gk104_hdmi_ctrl, + .head.new = gf119_head_new, + .dac = { .nr = 3, .new = gf119_dac_new }, + .sor = { .nr = 4, .new = gm107_sor_new }, }; int diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c index 9f368d4ee61e..292d3b5f9704 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c @@ -22,6 +22,8 @@ * Authors: Ben Skeggs */ #include "nv50.h" +#include "head.h" +#include "ior.h" #include "rootnv50.h" static const struct nv50_disp_func @@ -29,23 +31,11 @@ gm200_disp = { .intr = gf119_disp_intr, .intr_error = gf119_disp_intr_error, .uevent = &gf119_disp_chan_uevent, - .super = gf119_disp_intr_supervisor, + .super = gf119_disp_super, .root = &gm200_disp_root_oclass, - .head.vblank_init = gf119_disp_vblank_init, - .head.vblank_fini = gf119_disp_vblank_fini, - .head.scanoutpos = gf119_disp_root_scanoutpos, - .outp.internal.crt = nv50_dac_output_new, - .outp.internal.tmds = nv50_sor_output_new, - .outp.internal.lvds = nv50_sor_output_new, - .outp.internal.dp = gm200_sor_dp_new, - .dac.nr = 3, - .dac.power = nv50_dac_power, - .dac.sense = nv50_dac_sense, - .sor.nr = 4, - .sor.power = nv50_sor_power, - .sor.hda_eld = gf119_hda_eld, - .sor.hdmi = gk104_hdmi_ctrl, - .sor.magic = gm200_sor_magic, + .head.new = gf119_head_new, + .dac = { .nr = 3, .new = gf119_dac_new }, + .sor = { .nr = 4, .new = gm200_sor_new }, }; int diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c index 4f81bf31435e..39eb98b2c3a2 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c @@ -22,6 +22,8 @@ * Authors: Ben Skeggs <bskeggs@redhat.com> */ #include "nv50.h" +#include "head.h" +#include "ior.h" #include "rootnv50.h" static const struct nv50_disp_func @@ -29,23 +31,10 @@ gp100_disp = { .intr = gf119_disp_intr, .intr_error = gf119_disp_intr_error, .uevent = &gf119_disp_chan_uevent, - .super = gf119_disp_intr_supervisor, + .super = gf119_disp_super, .root = &gp100_disp_root_oclass, - .head.vblank_init = gf119_disp_vblank_init, - .head.vblank_fini = gf119_disp_vblank_fini, - .head.scanoutpos = gf119_disp_root_scanoutpos, - .outp.internal.crt = nv50_dac_output_new, - .outp.internal.tmds = nv50_sor_output_new, - .outp.internal.lvds = nv50_sor_output_new, - .outp.internal.dp = gm200_sor_dp_new, - .dac.nr = 3, - .dac.power = nv50_dac_power, - .dac.sense = nv50_dac_sense, - .sor.nr = 4, - .sor.power = nv50_sor_power, - .sor.hda_eld = gf119_hda_eld, - .sor.hdmi = gk104_hdmi_ctrl, - .sor.magic = gm200_sor_magic, + .head.new = gf119_head_new, + .sor = { .nr = 4, .new = gm200_sor_new }, }; int diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp102.c index f5d613f82709..91d70fe18275 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp102.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp102.c @@ -22,6 +22,8 @@ * Authors: Ben Skeggs <bskeggs@redhat.com> */ #include "nv50.h" +#include "head.h" +#include "ior.h" #include "rootnv50.h" static void @@ -55,23 +57,10 @@ gp102_disp = { .intr = gf119_disp_intr, .intr_error = gp102_disp_intr_error, .uevent = &gf119_disp_chan_uevent, - .super = gf119_disp_intr_supervisor, + .super = gf119_disp_super, .root = &gp102_disp_root_oclass, - .head.vblank_init = gf119_disp_vblank_init, - .head.vblank_fini = gf119_disp_vblank_fini, - .head.scanoutpos = gf119_disp_root_scanoutpos, - .outp.internal.crt = nv50_dac_output_new, - .outp.internal.tmds = nv50_sor_output_new, - .outp.internal.lvds = nv50_sor_output_new, - .outp.internal.dp = gm200_sor_dp_new, - .dac.nr = 3, - .dac.power = nv50_dac_power, - .dac.sense = nv50_dac_sense, - .sor.nr = 4, - .sor.power = nv50_sor_power, - .sor.hda_eld = gf119_hda_eld, - .sor.hdmi = gk104_hdmi_ctrl, - .sor.magic = gm200_sor_magic, + .head.new = gf119_head_new, + .sor = { .nr = 4, .new = gm200_sor_new }, }; int diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt200.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt200.c index 6bc3bf096001..bf00c4e3be3a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt200.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt200.c @@ -22,30 +22,20 @@ * Authors: Ben Skeggs */ #include "nv50.h" +#include "head.h" +#include "ior.h" #include "rootnv50.h" static const struct nv50_disp_func gt200_disp = { .intr = nv50_disp_intr, .uevent = &nv50_disp_chan_uevent, - .super = nv50_disp_intr_supervisor, + .super = nv50_disp_super, .root = >200_disp_root_oclass, - .head.vblank_init = nv50_disp_vblank_init, - .head.vblank_fini = nv50_disp_vblank_fini, - .head.scanoutpos = nv50_disp_root_scanoutpos, - .outp.internal.crt = nv50_dac_output_new, - .outp.internal.tmds = nv50_sor_output_new, - .outp.internal.lvds = nv50_sor_output_new, - .outp.external.tmds = nv50_pior_output_new, - .outp.external.dp = nv50_pior_dp_new, - .dac.nr = 3, - .dac.power = nv50_dac_power, - .dac.sense = nv50_dac_sense, - .sor.nr = 2, - .sor.power = nv50_sor_power, - .sor.hdmi = g84_hdmi_ctrl, - .pior.nr = 3, - .pior.power = nv50_pior_power, + .head.new = nv50_head_new, + .dac = { .nr = 3, .new = nv50_dac_new }, + .sor = { .nr = 2, .new = g84_sor_new }, + .pior = { .nr = 3, .new = nv50_pior_new }, }; int diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c index 94026288ab4d..2cdd4d7a98d3 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c @@ -22,32 +22,20 @@ * Authors: Ben Skeggs */ #include "nv50.h" +#include "head.h" +#include "ior.h" #include "rootnv50.h" static const struct nv50_disp_func gt215_disp = { .intr = nv50_disp_intr, .uevent = &nv50_disp_chan_uevent, - .super = nv50_disp_intr_supervisor, + .super = nv50_disp_super, .root = >215_disp_root_oclass, - .head.vblank_init = nv50_disp_vblank_init, - .head.vblank_fini = nv50_disp_vblank_fini, - .head.scanoutpos = nv50_disp_root_scanoutpos, - .outp.internal.crt = nv50_dac_output_new, - .outp.internal.tmds = nv50_sor_output_new, - .outp.internal.lvds = nv50_sor_output_new, - .outp.internal.dp = g94_sor_dp_new, - .outp.external.tmds = nv50_pior_output_new, - .outp.external.dp = nv50_pior_dp_new, - .dac.nr = 3, - .dac.power = nv50_dac_power, - .dac.sense = nv50_dac_sense, - .sor.nr = 4, - .sor.power = nv50_sor_power, - .sor.hda_eld = gt215_hda_eld, - .sor.hdmi = gt215_hdmi_ctrl, - .pior.nr = 3, - .pior.power = nv50_pior_power, + .head.new = nv50_head_new, + .dac = { .nr = 3, .new = nv50_dac_new }, + .sor = { .nr = 4, .new = gt215_sor_new }, + .pior = { .nr = 3, .new = nv50_pior_new }, }; int diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagf119.c index da6129b2b78f..0fa0ec0a1de0 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagf119.c @@ -21,63 +21,34 @@ * * Authors: Ben Skeggs */ -#include "nv50.h" -#include "outp.h" +#include "ior.h" -#include <core/client.h> -#include <subdev/bios.h> -#include <subdev/bios/dcb.h> -#include <subdev/timer.h> - -#include <nvif/cl5070.h> -#include <nvif/unpack.h> - -int -gf119_hda_eld(NV50_DISP_MTHD_V1) +void +gf119_hda_eld(struct nvkm_ior *ior, u8 *data, u8 size) { - struct nvkm_device *device = disp->base.engine.subdev.device; - union { - struct nv50_disp_sor_hda_eld_v0 v0; - } *args = data; - const u32 soff = outp->or * 0x030; - const u32 hoff = head * 0x800; - int ret = -ENOSYS, i; + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 soff = 0x030 * ior->id; + int i; - nvif_ioctl(object, "disp sor hda eld size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { - nvif_ioctl(object, "disp sor hda eld vers %d\n", - args->v0.version); - if (size > 0x60) - return -E2BIG; - } else - return ret; + for (i = 0; i < size; i++) + nvkm_wr32(device, 0x10ec00 + soff, (i << 8) | data[i]); + for (; i < 0x60; i++) + nvkm_wr32(device, 0x10ec00 + soff, (i << 8)); + nvkm_mask(device, 0x10ec10 + soff, 0x80000002, 0x80000002); +} - if (size && args->v0.data[0]) { - if (outp->info.type == DCB_OUTPUT_DP) { - nvkm_mask(device, 0x616618 + hoff, 0x8000000c, 0x80000001); - nvkm_msec(device, 2000, - u32 tmp = nvkm_rd32(device, 0x616618 + hoff); - if (!(tmp & 0x80000000)) - break; - ); - } +void +gf119_hda_hpd(struct nvkm_ior *ior, int head, bool present) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 hoff = 0x800 * head; + u32 data = 0x80000000; + u32 mask = 0x80000001; + if (present) { nvkm_mask(device, 0x616548 + hoff, 0x00000070, 0x00000000); - for (i = 0; i < size; i++) - nvkm_wr32(device, 0x10ec00 + soff, (i << 8) | args->v0.data[i]); - for (; i < 0x60; i++) - nvkm_wr32(device, 0x10ec00 + soff, (i << 8)); - nvkm_mask(device, 0x10ec10 + soff, 0x80000003, 0x80000003); + data |= 0x00000001; } else { - if (outp->info.type == DCB_OUTPUT_DP) { - nvkm_mask(device, 0x616618 + hoff, 0x80000001, 0x80000000); - nvkm_msec(device, 2000, - u32 tmp = nvkm_rd32(device, 0x616618 + hoff); - if (!(tmp & 0x80000000)) - break; - ); - } - nvkm_mask(device, 0x10ec10 + soff, 0x80000003, 0x80000000 | !!size); + mask |= 0x00000002; } - - return 0; + nvkm_mask(device, 0x10ec10 + ior->id * 0x030, mask, data); } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c index f8f2f16c22a2..4509d2ba880e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c @@ -21,59 +21,31 @@ * * Authors: Ben Skeggs */ -#include "nv50.h" -#include "outp.h" +#include "ior.h" -#include <core/client.h> -#include <subdev/timer.h> - -#include <nvif/cl5070.h> -#include <nvif/unpack.h> - -int -gt215_hda_eld(NV50_DISP_MTHD_V1) +void +gt215_hda_eld(struct nvkm_ior *ior, u8 *data, u8 size) { - struct nvkm_device *device = disp->base.engine.subdev.device; - union { - struct nv50_disp_sor_hda_eld_v0 v0; - } *args = data; - const u32 soff = outp->or * 0x800; - int ret = -ENOSYS, i; - - nvif_ioctl(object, "disp sor hda eld size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { - nvif_ioctl(object, "disp sor hda eld vers %d\n", - args->v0.version); - if (size > 0x60) - return -E2BIG; - } else - return ret; + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 soff = ior->id * 0x800; + int i; - if (size && args->v0.data[0]) { - if (outp->info.type == DCB_OUTPUT_DP) { - nvkm_mask(device, 0x61c1e0 + soff, 0x8000000d, 0x80000001); - nvkm_msec(device, 2000, - u32 tmp = nvkm_rd32(device, 0x61c1e0 + soff); - if (!(tmp & 0x80000000)) - break; - ); - } - for (i = 0; i < size; i++) - nvkm_wr32(device, 0x61c440 + soff, (i << 8) | args->v0.data[i]); - for (; i < 0x60; i++) - nvkm_wr32(device, 0x61c440 + soff, (i << 8)); - nvkm_mask(device, 0x61c448 + soff, 0x80000003, 0x80000003); - } else { - if (outp->info.type == DCB_OUTPUT_DP) { - nvkm_mask(device, 0x61c1e0 + soff, 0x80000001, 0x80000000); - nvkm_msec(device, 2000, - u32 tmp = nvkm_rd32(device, 0x61c1e0 + soff); - if (!(tmp & 0x80000000)) - break; - ); - } - nvkm_mask(device, 0x61c448 + soff, 0x80000003, 0x80000000 | !!size); - } + for (i = 0; i < size; i++) + nvkm_wr32(device, 0x61c440 + soff, (i << 8) | data[i]); + for (; i < 0x60; i++) + nvkm_wr32(device, 0x61c440 + soff, (i << 8)); + nvkm_mask(device, 0x61c448 + soff, 0x80000002, 0x80000002); +} - return 0; +void +gt215_hda_hpd(struct nvkm_ior *ior, int head, bool present) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + u32 data = 0x80000000; + u32 mask = 0x80000001; + if (present) + data |= 0x00000001; + else + mask |= 0x00000002; + nvkm_mask(device, 0x61c448 + ior->id * 0x800, mask, data); } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.c new file mode 100644 index 000000000000..e82c68f18444 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.c @@ -0,0 +1,66 @@ +#include "hdmi.h" + +void pack_hdmi_infoframe(struct packed_hdmi_infoframe *packed_frame, + u8 *raw_frame, ssize_t len) +{ + u32 header = 0; + u32 subpack0_low = 0; + u32 subpack0_high = 0; + u32 subpack1_low = 0; + u32 subpack1_high = 0; + + switch (len) { + /* + * "When in doubt, use brute force." + * -- Ken Thompson. + */ + default: + /* + * We presume that no valid frame is longer than 17 + * octets, including header... And truncate to that + * if it's longer. + */ + case 17: + subpack1_high = (raw_frame[16] << 16); + case 16: + subpack1_high |= (raw_frame[15] << 8); + case 15: + subpack1_high |= raw_frame[14]; + case 14: + subpack1_low = (raw_frame[13] << 24); + case 13: + subpack1_low |= (raw_frame[12] << 16); + case 12: + subpack1_low |= (raw_frame[11] << 8); + case 11: + subpack1_low |= raw_frame[10]; + case 10: + subpack0_high = (raw_frame[9] << 16); + case 9: + subpack0_high |= (raw_frame[8] << 8); + case 8: + subpack0_high |= raw_frame[7]; + case 7: + subpack0_low = (raw_frame[6] << 24); + case 6: + subpack0_low |= (raw_frame[5] << 16); + case 5: + subpack0_low |= (raw_frame[4] << 8); + case 4: + subpack0_low |= raw_frame[3]; + case 3: + header = (raw_frame[2] << 16); + case 2: + header |= (raw_frame[1] << 8); + case 1: + header |= raw_frame[0]; + case 0: + break; + } + + packed_frame->header = header; + packed_frame->subpack0_low = subpack0_low; + packed_frame->subpack0_high = subpack0_high; + packed_frame->subpack1_low = subpack1_low; + packed_frame->subpack1_high = subpack1_high; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.h new file mode 100644 index 000000000000..528f5621a496 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.h @@ -0,0 +1,15 @@ +#ifndef __NVKM_DISP_HDMI_H__ +#define __NVKM_DISP_HDMI_H__ +#include "ior.h" + +struct packed_hdmi_infoframe { + u32 header; + u32 subpack0_low; + u32 subpack0_high; + u32 subpack1_low; + u32 subpack1_high; +}; + +void pack_hdmi_infoframe(struct packed_hdmi_infoframe *packed_frame, + u8 *raw_frame, ssize_t len); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c index 1c4256e8cbd6..661410f9b457 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c @@ -21,54 +21,42 @@ * * Authors: Ben Skeggs */ -#include "nv50.h" +#include "hdmi.h" -#include <core/client.h> - -#include <nvif/cl5070.h> -#include <nvif/unpack.h> - -int -g84_hdmi_ctrl(NV50_DISP_MTHD_V1) +void +g84_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet, + u8 rekey, u8 *avi, u8 avi_size, u8 *vendor, u8 vendor_size) { - struct nvkm_device *device = disp->base.engine.subdev.device; - const u32 hoff = (head * 0x800); - union { - struct nv50_disp_sor_hdmi_pwr_v0 v0; - } *args = data; - u32 ctrl; - int ret = -ENOSYS; + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 ctrl = 0x40000000 * enable | + 0x1f000000 /* ??? */ | + max_ac_packet << 16 | + rekey; + const u32 hoff = head * 0x800; + struct packed_hdmi_infoframe avi_infoframe; + struct packed_hdmi_infoframe vendor_infoframe; - nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { - nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " - "max_ac_packet %d rekey %d\n", - args->v0.version, args->v0.state, - args->v0.max_ac_packet, args->v0.rekey); - if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f) - return -EINVAL; - ctrl = 0x40000000 * !!args->v0.state; - ctrl |= args->v0.max_ac_packet << 16; - ctrl |= args->v0.rekey; - ctrl |= 0x1f000000; /* ??? */ - } else - return ret; + pack_hdmi_infoframe(&avi_infoframe, avi, avi_size); + pack_hdmi_infoframe(&vendor_infoframe, vendor, vendor_size); if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x6165a4 + hoff, 0x40000000, 0x00000000); + nvkm_mask(device, 0x61653c + hoff, 0x00000001, 0x00000000); nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000000); nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000000); - return 0; + return; } /* AVI InfoFrame */ nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000000); - nvkm_wr32(device, 0x616528 + hoff, 0x000d0282); - nvkm_wr32(device, 0x61652c + hoff, 0x0000006f); - nvkm_wr32(device, 0x616530 + hoff, 0x00000000); - nvkm_wr32(device, 0x616534 + hoff, 0x00000000); - nvkm_wr32(device, 0x616538 + hoff, 0x00000000); - nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000001); + if (avi_size) { + nvkm_wr32(device, 0x616528 + hoff, avi_infoframe.header); + nvkm_wr32(device, 0x61652c + hoff, avi_infoframe.subpack0_low); + nvkm_wr32(device, 0x616530 + hoff, avi_infoframe.subpack0_high); + nvkm_wr32(device, 0x616534 + hoff, avi_infoframe.subpack1_low); + nvkm_wr32(device, 0x616538 + hoff, avi_infoframe.subpack1_high); + nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000001); + } /* Audio InfoFrame */ nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000000); @@ -77,6 +65,18 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1) nvkm_wr32(device, 0x616510 + hoff, 0x00000000); nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000001); + /* Vendor InfoFrame */ + nvkm_mask(device, 0x61653c + hoff, 0x00010001, 0x00010000); + if (vendor_size) { + nvkm_wr32(device, 0x616544 + hoff, vendor_infoframe.header); + nvkm_wr32(device, 0x616548 + hoff, vendor_infoframe.subpack0_low); + nvkm_wr32(device, 0x61654c + hoff, vendor_infoframe.subpack0_high); + /* Is there a second (or up to fourth?) set of subpack registers here? */ + /* nvkm_wr32(device, 0x616550 + hoff, vendor_infoframe->subpack1_low); */ + /* nvkm_wr32(device, 0x616554 + hoff, vendor_infoframe->subpack1_high); */ + nvkm_mask(device, 0x61653c + hoff, 0x00010001, 0x00010001); + } + nvkm_mask(device, 0x6165d0 + hoff, 0x00070001, 0x00010001); /* SPARE, HW_CTS */ nvkm_mask(device, 0x616568 + hoff, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */ nvkm_mask(device, 0x616578 + hoff, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */ @@ -88,5 +88,4 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1) /* HDMI_CTRL */ nvkm_mask(device, 0x6165a4 + hoff, 0x5f1f007f, ctrl); - return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c index 632f02da1382..6cac0e72b4cc 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c @@ -21,53 +21,56 @@ * * Authors: Ben Skeggs */ -#include "nv50.h" +#include "hdmi.h" -#include <core/client.h> - -#include <nvif/cl5070.h> -#include <nvif/unpack.h> - -int -gf119_hdmi_ctrl(NV50_DISP_MTHD_V1) +void +gf119_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet, + u8 rekey, u8 *avi, u8 avi_size, u8 *vendor, u8 vendor_size) { - struct nvkm_device *device = disp->base.engine.subdev.device; - const u32 hoff = (head * 0x800); - union { - struct nv50_disp_sor_hdmi_pwr_v0 v0; - } *args = data; - u32 ctrl; - int ret = -ENOSYS; + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 ctrl = 0x40000000 * enable | + max_ac_packet << 16 | + rekey; + const u32 hoff = head * 0x800; + struct packed_hdmi_infoframe avi_infoframe; + struct packed_hdmi_infoframe vendor_infoframe; - nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { - nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " - "max_ac_packet %d rekey %d\n", - args->v0.version, args->v0.state, - args->v0.max_ac_packet, args->v0.rekey); - if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f) - return -EINVAL; - ctrl = 0x40000000 * !!args->v0.state; - ctrl |= args->v0.max_ac_packet << 16; - ctrl |= args->v0.rekey; - } else - return ret; + pack_hdmi_infoframe(&avi_infoframe, avi, avi_size); + pack_hdmi_infoframe(&vendor_infoframe, vendor, vendor_size); if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000); + nvkm_mask(device, 0x616730 + hoff, 0x00000001, 0x00000000); nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000000); nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000000); - return 0; + return; } /* AVI InfoFrame */ nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000000); - nvkm_wr32(device, 0x61671c + hoff, 0x000d0282); - nvkm_wr32(device, 0x616720 + hoff, 0x0000006f); - nvkm_wr32(device, 0x616724 + hoff, 0x00000000); - nvkm_wr32(device, 0x616728 + hoff, 0x00000000); - nvkm_wr32(device, 0x61672c + hoff, 0x00000000); - nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000001); + if (avi_size) { + nvkm_wr32(device, 0x61671c + hoff, avi_infoframe.header); + nvkm_wr32(device, 0x616720 + hoff, avi_infoframe.subpack0_low); + nvkm_wr32(device, 0x616724 + hoff, avi_infoframe.subpack0_high); + nvkm_wr32(device, 0x616728 + hoff, avi_infoframe.subpack1_low); + nvkm_wr32(device, 0x61672c + hoff, avi_infoframe.subpack1_high); + nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000001); + } + + /* GENERIC(?) / Vendor InfoFrame? */ + nvkm_mask(device, 0x616730 + hoff, 0x00010001, 0x00010000); + if (vendor_size) { + /* + * These appear to be the audio infoframe registers, + * but no other set of infoframe registers has yet + * been found. + */ + nvkm_wr32(device, 0x616738 + hoff, vendor_infoframe.header); + nvkm_wr32(device, 0x61673c + hoff, vendor_infoframe.subpack0_low); + nvkm_wr32(device, 0x616740 + hoff, vendor_infoframe.subpack0_high); + /* Is there a second (or further?) set of subpack registers here? */ + nvkm_mask(device, 0x616730 + hoff, 0x00000001, 0x00000001); + } /* ??? InfoFrame? */ nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000000); @@ -76,5 +79,4 @@ gf119_hdmi_ctrl(NV50_DISP_MTHD_V1) /* HDMI_CTRL */ nvkm_mask(device, 0x616798 + hoff, 0x401f007f, ctrl); - return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c index 4e8067d511d7..ed0a6100d76b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c @@ -21,54 +21,53 @@ * * Authors: Ben Skeggs */ -#include "nv50.h" +#include "hdmi.h" -#include <core/client.h> - -#include <nvif/cl5070.h> -#include <nvif/unpack.h> - -int -gk104_hdmi_ctrl(NV50_DISP_MTHD_V1) +void +gk104_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet, + u8 rekey, u8 *avi, u8 avi_size, u8 *vendor, u8 vendor_size) { - struct nvkm_device *device = disp->base.engine.subdev.device; - const u32 hoff = (head * 0x800); - const u32 hdmi = (head * 0x400); - union { - struct nv50_disp_sor_hdmi_pwr_v0 v0; - } *args = data; - u32 ctrl; - int ret = -ENOSYS; + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 ctrl = 0x40000000 * enable | + max_ac_packet << 16 | + rekey; + const u32 hoff = head * 0x800; + const u32 hdmi = head * 0x400; + struct packed_hdmi_infoframe avi_infoframe; + struct packed_hdmi_infoframe vendor_infoframe; - nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { - nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " - "max_ac_packet %d rekey %d\n", - args->v0.version, args->v0.state, - args->v0.max_ac_packet, args->v0.rekey); - if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f) - return -EINVAL; - ctrl = 0x40000000 * !!args->v0.state; - ctrl |= args->v0.max_ac_packet << 16; - ctrl |= args->v0.rekey; - } else - return ret; + pack_hdmi_infoframe(&avi_infoframe, avi, avi_size); + pack_hdmi_infoframe(&vendor_infoframe, vendor, vendor_size); if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000); + nvkm_mask(device, 0x690100 + hdmi, 0x00000001, 0x00000000); nvkm_mask(device, 0x6900c0 + hdmi, 0x00000001, 0x00000000); nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000000); - return 0; + return; } /* AVI InfoFrame */ nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000000); - nvkm_wr32(device, 0x690008 + hdmi, 0x000d0282); - nvkm_wr32(device, 0x69000c + hdmi, 0x0000006f); - nvkm_wr32(device, 0x690010 + hdmi, 0x00000000); - nvkm_wr32(device, 0x690014 + hdmi, 0x00000000); - nvkm_wr32(device, 0x690018 + hdmi, 0x00000000); - nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000001); + if (avi_size) { + nvkm_wr32(device, 0x690008 + hdmi, avi_infoframe.header); + nvkm_wr32(device, 0x69000c + hdmi, avi_infoframe.subpack0_low); + nvkm_wr32(device, 0x690010 + hdmi, avi_infoframe.subpack0_high); + nvkm_wr32(device, 0x690014 + hdmi, avi_infoframe.subpack1_low); + nvkm_wr32(device, 0x690018 + hdmi, avi_infoframe.subpack1_high); + nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000001); + } + + /* GENERIC(?) / Vendor InfoFrame? */ + nvkm_mask(device, 0x690100 + hdmi, 0x00010001, 0x00000000); + if (vendor_size) { + nvkm_wr32(device, 0x690108 + hdmi, vendor_infoframe.header); + nvkm_wr32(device, 0x69010c + hdmi, vendor_infoframe.subpack0_low); + nvkm_wr32(device, 0x690110 + hdmi, vendor_infoframe.subpack0_high); + /* Is there a second (or further?) set of subpack registers here? */ + nvkm_mask(device, 0x690100 + hdmi, 0x00000001, 0x00000001); + } + /* ??? InfoFrame? */ nvkm_mask(device, 0x6900c0 + hdmi, 0x00000001, 0x00000000); @@ -80,5 +79,4 @@ gk104_hdmi_ctrl(NV50_DISP_MTHD_V1) /* HDMI_CTRL */ nvkm_mask(device, 0x616798 + hoff, 0x401f007f, ctrl); - return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c index f1afc16494b6..0993d223bb9c 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c @@ -21,55 +21,42 @@ * * Authors: Ben Skeggs */ -#include "nv50.h" -#include "outp.h" +#include "hdmi.h" -#include <core/client.h> - -#include <nvif/cl5070.h> -#include <nvif/unpack.h> - -int -gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) +void +gt215_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet, + u8 rekey, u8 *avi, u8 avi_size, u8 *vendor, u8 vendor_size) { - struct nvkm_device *device = disp->base.engine.subdev.device; - const u32 soff = outp->or * 0x800; - union { - struct nv50_disp_sor_hdmi_pwr_v0 v0; - } *args = data; - u32 ctrl; - int ret = -ENOSYS; + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 ctrl = 0x40000000 * enable | + 0x1f000000 /* ??? */ | + max_ac_packet << 16 | + rekey; + const u32 soff = nv50_ior_base(ior); + struct packed_hdmi_infoframe avi_infoframe; + struct packed_hdmi_infoframe vendor_infoframe; - nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { - nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " - "max_ac_packet %d rekey %d\n", - args->v0.version, args->v0.state, - args->v0.max_ac_packet, args->v0.rekey); - if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f) - return -EINVAL; - ctrl = 0x40000000 * !!args->v0.state; - ctrl |= args->v0.max_ac_packet << 16; - ctrl |= args->v0.rekey; - ctrl |= 0x1f000000; /* ??? */ - } else - return ret; + pack_hdmi_infoframe(&avi_infoframe, avi, avi_size); + pack_hdmi_infoframe(&vendor_infoframe, vendor, vendor_size); if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x61c5a4 + soff, 0x40000000, 0x00000000); + nvkm_mask(device, 0x61c53c + soff, 0x00000001, 0x00000000); nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000); nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000000); - return 0; + return; } /* AVI InfoFrame */ nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000); - nvkm_wr32(device, 0x61c528 + soff, 0x000d0282); - nvkm_wr32(device, 0x61c52c + soff, 0x0000006f); - nvkm_wr32(device, 0x61c530 + soff, 0x00000000); - nvkm_wr32(device, 0x61c534 + soff, 0x00000000); - nvkm_wr32(device, 0x61c538 + soff, 0x00000000); - nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000001); + if (avi_size) { + nvkm_wr32(device, 0x61c528 + soff, avi_infoframe.header); + nvkm_wr32(device, 0x61c52c + soff, avi_infoframe.subpack0_low); + nvkm_wr32(device, 0x61c530 + soff, avi_infoframe.subpack0_high); + nvkm_wr32(device, 0x61c534 + soff, avi_infoframe.subpack1_low); + nvkm_wr32(device, 0x61c538 + soff, avi_infoframe.subpack1_high); + nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000001); + } /* Audio InfoFrame */ nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000000); @@ -78,6 +65,18 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) nvkm_wr32(device, 0x61c510 + soff, 0x00000000); nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000001); + /* Vendor InfoFrame */ + nvkm_mask(device, 0x61c53c + soff, 0x00010001, 0x00010000); + if (vendor_size) { + nvkm_wr32(device, 0x61c544 + soff, vendor_infoframe.header); + nvkm_wr32(device, 0x61c548 + soff, vendor_infoframe.subpack0_low); + nvkm_wr32(device, 0x61c54c + soff, vendor_infoframe.subpack0_high); + /* Is there a second (or up to fourth?) set of subpack registers here? */ + /* nvkm_wr32(device, 0x61c550 + soff, vendor_infoframe.subpack1_low); */ + /* nvkm_wr32(device, 0x61c554 + soff, vendor_infoframe.subpack1_high); */ + nvkm_mask(device, 0x61c53c + soff, 0x00010001, 0x00010001); + } + nvkm_mask(device, 0x61c5d0 + soff, 0x00070001, 0x00010001); /* SPARE, HW_CTS */ nvkm_mask(device, 0x61c568 + soff, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */ nvkm_mask(device, 0x61c578 + soff, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */ @@ -89,5 +88,4 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) /* HDMI_CTRL */ nvkm_mask(device, 0x61c5a4 + soff, 0x5f1f007f, ctrl); - return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.c new file mode 100644 index 000000000000..5c557f3e6c2c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.c @@ -0,0 +1,105 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * 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 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 HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Ben Skeggs <bskeggs@redhat.com> + */ +#include "head.h" + +#include <core/client.h> + +#include <nvif/cl0046.h> +#include <nvif/unpack.h> + +struct nvkm_head * +nvkm_head_find(struct nvkm_disp *disp, int id) +{ + struct nvkm_head *head; + list_for_each_entry(head, &disp->head, head) { + if (head->id == id) + return head; + } + return NULL; +} + +int +nvkm_head_mthd_scanoutpos(struct nvkm_object *object, + struct nvkm_head *head, void *data, u32 size) +{ + union { + struct nv04_disp_scanoutpos_v0 v0; + } *args = data; + int ret = -ENOSYS; + + nvif_ioctl(object, "head scanoutpos size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(object, "head scanoutpos vers %d\n", + args->v0.version); + + head->func->state(head, &head->arm); + args->v0.vtotal = head->arm.vtotal; + args->v0.vblanks = head->arm.vblanks; + args->v0.vblanke = head->arm.vblanke; + args->v0.htotal = head->arm.htotal; + args->v0.hblanks = head->arm.hblanks; + args->v0.hblanke = head->arm.hblanke; + + /* We don't support reading htotal/vtotal on pre-NV50 VGA, + * so we have to give up and trigger the timestamping + * fallback in the drm core. + */ + if (!args->v0.vtotal || !args->v0.htotal) + return -ENOTSUPP; + + args->v0.time[0] = ktime_to_ns(ktime_get()); + head->func->rgpos(head, &args->v0.hline, &args->v0.vline); + args->v0.time[1] = ktime_to_ns(ktime_get()); + } else + return ret; + + return 0; +} + +void +nvkm_head_del(struct nvkm_head **phead) +{ + struct nvkm_head *head = *phead; + if (head) { + HEAD_DBG(head, "dtor"); + list_del(&head->head); + kfree(*phead); + *phead = NULL; + } +} + +int +nvkm_head_new_(const struct nvkm_head_func *func, + struct nvkm_disp *disp, int id) +{ + struct nvkm_head *head; + if (!(head = kzalloc(sizeof(*head), GFP_KERNEL))) + return -ENOMEM; + head->func = func; + head->disp = disp; + head->id = id; + list_add_tail(&head->head, &disp->head); + HEAD_DBG(head, "ctor"); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h new file mode 100644 index 000000000000..b04c49d2eeeb --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h @@ -0,0 +1,56 @@ +#ifndef __NVKM_DISP_HEAD_H__ +#define __NVKM_DISP_HEAD_H__ +#include "priv.h" + +struct nvkm_head { + const struct nvkm_head_func *func; + struct nvkm_disp *disp; + int id; + + struct list_head head; + + struct nvkm_head_state { + u16 htotal; + u16 hsynce; + u16 hblanke; + u16 hblanks; + u16 vtotal; + u16 vsynce; + u16 vblanke; + u16 vblanks; + u32 hz; + + /* Prior to GF119, these are set by the OR. */ + struct { + u8 depth; + } or; + } arm, asy; +}; + +int nvkm_head_new_(const struct nvkm_head_func *, struct nvkm_disp *, int id); +void nvkm_head_del(struct nvkm_head **); +int nvkm_head_mthd_scanoutpos(struct nvkm_object *, + struct nvkm_head *, void *, u32); +struct nvkm_head *nvkm_head_find(struct nvkm_disp *, int id); + +struct nvkm_head_func { + void (*state)(struct nvkm_head *, struct nvkm_head_state *); + void (*rgpos)(struct nvkm_head *, u16 *hline, u16 *vline); + void (*rgclk)(struct nvkm_head *, int div); + void (*vblank_get)(struct nvkm_head *); + void (*vblank_put)(struct nvkm_head *); +}; + +void nv50_head_rgpos(struct nvkm_head *, u16 *, u16 *); + +#define HEAD_MSG(h,l,f,a...) do { \ + struct nvkm_head *_h = (h); \ + nvkm_##l(&_h->disp->engine.subdev, "head-%d: "f"\n", _h->id, ##a); \ +} while(0) +#define HEAD_WARN(h,f,a...) HEAD_MSG((h), warn, f, ##a) +#define HEAD_DBG(h,f,a...) HEAD_MSG((h), debug, f, ##a) + +int nv04_head_new(struct nvkm_disp *, int id); +int nv50_head_new(struct nvkm_disp *, int id); +int gf119_head_new(struct nvkm_disp *, int id); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/headgf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/headgf119.c new file mode 100644 index 000000000000..b33552757647 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/headgf119.c @@ -0,0 +1,96 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * 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 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 HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Ben Skeggs <bskeggs@redhat.com> + */ +#include "head.h" + +static void +gf119_head_vblank_put(struct nvkm_head *head) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + const u32 hoff = head->id * 0x800; + nvkm_mask(device, 0x6100c0 + hoff, 0x00000001, 0x00000000); +} + +static void +gf119_head_vblank_get(struct nvkm_head *head) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + const u32 hoff = head->id * 0x800; + nvkm_mask(device, 0x6100c0 + hoff, 0x00000001, 0x00000001); +} + +static void +gf119_head_rgclk(struct nvkm_head *head, int div) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + nvkm_mask(device, 0x612200 + (head->id * 0x800), 0x0000000f, div); +} + +static void +gf119_head_state(struct nvkm_head *head, struct nvkm_head_state *state) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + const u32 hoff = (state == &head->asy) * 0x20000 + head->id * 0x300; + u32 data; + + data = nvkm_rd32(device, 0x640414 + hoff); + state->vtotal = (data & 0xffff0000) >> 16; + state->htotal = (data & 0x0000ffff); + data = nvkm_rd32(device, 0x640418 + hoff); + state->vsynce = (data & 0xffff0000) >> 16; + state->hsynce = (data & 0x0000ffff); + data = nvkm_rd32(device, 0x64041c + hoff); + state->vblanke = (data & 0xffff0000) >> 16; + state->hblanke = (data & 0x0000ffff); + data = nvkm_rd32(device, 0x640420 + hoff); + state->vblanks = (data & 0xffff0000) >> 16; + state->hblanks = (data & 0x0000ffff); + state->hz = nvkm_rd32(device, 0x640450 + hoff); + + data = nvkm_rd32(device, 0x640404 + hoff); + switch ((data & 0x000003c0) >> 6) { + case 6: state->or.depth = 30; break; + case 5: state->or.depth = 24; break; + case 2: state->or.depth = 18; break; + case 0: state->or.depth = 18; break; /*XXX: "default" */ + default: + state->or.depth = 18; + WARN_ON(1); + break; + } +} + +static const struct nvkm_head_func +gf119_head = { + .state = gf119_head_state, + .rgpos = nv50_head_rgpos, + .rgclk = gf119_head_rgclk, + .vblank_get = gf119_head_vblank_get, + .vblank_put = gf119_head_vblank_put, +}; + +int +gf119_head_new(struct nvkm_disp *disp, int id) +{ + return nvkm_head_new_(&gf119_head, disp, id); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv04.c new file mode 100644 index 000000000000..dcf459282aa1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv04.c @@ -0,0 +1,74 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * 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 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 HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Ben Skeggs <bskeggs@redhat.com> + */ +#include "head.h" + +static void +nv04_head_vblank_put(struct nvkm_head *head) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + nvkm_wr32(device, 0x600140 + (head->id * 0x2000) , 0x00000000); +} + +static void +nv04_head_vblank_get(struct nvkm_head *head) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + nvkm_wr32(device, 0x600140 + (head->id * 0x2000) , 0x00000001); +} + +static void +nv04_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + u32 data = nvkm_rd32(device, 0x600868 + (head->id * 0x2000)); + *hline = (data & 0xffff0000) >> 16; + *vline = (data & 0x0000ffff); +} + +static void +nv04_head_state(struct nvkm_head *head, struct nvkm_head_state *state) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + const u32 hoff = head->id * 0x0200; + state->vblanks = nvkm_rd32(device, 0x680800 + hoff) & 0x0000ffff; + state->vtotal = nvkm_rd32(device, 0x680804 + hoff) & 0x0000ffff; + state->vblanke = state->vtotal - 1; + state->hblanks = nvkm_rd32(device, 0x680820 + hoff) & 0x0000ffff; + state->htotal = nvkm_rd32(device, 0x680824 + hoff) & 0x0000ffff; + state->hblanke = state->htotal - 1; +} + +static const struct nvkm_head_func +nv04_head = { + .state = nv04_head_state, + .rgpos = nv04_head_rgpos, + .vblank_get = nv04_head_vblank_get, + .vblank_put = nv04_head_vblank_put, +}; + +int +nv04_head_new(struct nvkm_disp *disp, int id) +{ + return nvkm_head_new_(&nv04_head, disp, id); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv50.c new file mode 100644 index 000000000000..c80d06d5168f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv50.c @@ -0,0 +1,92 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * 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 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 HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Ben Skeggs <bskeggs@redhat.com> + */ +#include "head.h" + +static void +nv50_head_vblank_put(struct nvkm_head *head) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + nvkm_mask(device, 0x61002c, (4 << head->id), 0); +} + +static void +nv50_head_vblank_get(struct nvkm_head *head) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + nvkm_mask(device, 0x61002c, (4 << head->id), (4 << head->id)); +} + +static void +nv50_head_rgclk(struct nvkm_head *head, int div) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + nvkm_mask(device, 0x614200 + (head->id * 0x800), 0x0000000f, div); +} + +void +nv50_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + const u32 hoff = head->id * 0x800; + /* vline read locks hline. */ + *vline = nvkm_rd32(device, 0x616340 + hoff) & 0x0000ffff; + *hline = nvkm_rd32(device, 0x616344 + hoff) & 0x0000ffff; +} + +static void +nv50_head_state(struct nvkm_head *head, struct nvkm_head_state *state) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + const u32 hoff = head->id * 0x540 + (state == &head->arm) * 4; + u32 data; + + data = nvkm_rd32(device, 0x610ae8 + hoff); + state->vblanke = (data & 0xffff0000) >> 16; + state->hblanke = (data & 0x0000ffff); + data = nvkm_rd32(device, 0x610af0 + hoff); + state->vblanks = (data & 0xffff0000) >> 16; + state->hblanks = (data & 0x0000ffff); + data = nvkm_rd32(device, 0x610af8 + hoff); + state->vtotal = (data & 0xffff0000) >> 16; + state->htotal = (data & 0x0000ffff); + data = nvkm_rd32(device, 0x610b00 + hoff); + state->vsynce = (data & 0xffff0000) >> 16; + state->hsynce = (data & 0x0000ffff); + state->hz = (nvkm_rd32(device, 0x610ad0 + hoff) & 0x003fffff) * 1000; +} + +static const struct nvkm_head_func +nv50_head = { + .state = nv50_head_state, + .rgpos = nv50_head_rgpos, + .rgclk = nv50_head_rgclk, + .vblank_get = nv50_head_vblank_get, + .vblank_put = nv50_head_vblank_put, +}; + +int +nv50_head_new(struct nvkm_disp *disp, int id) +{ + return nvkm_head_new_(&nv50_head, disp, id); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.c new file mode 100644 index 000000000000..a475ea56795c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.c @@ -0,0 +1,72 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * 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 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 HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Ben Skeggs <bskeggs@redhat.com> + */ +#include "ior.h" + +static const char * +nvkm_ior_name[] = { + [DAC] = "DAC", + [SOR] = "SOR", + [PIOR] = "PIOR", +}; + +struct nvkm_ior * +nvkm_ior_find(struct nvkm_disp *disp, enum nvkm_ior_type type, int id) +{ + struct nvkm_ior *ior; + list_for_each_entry(ior, &disp->ior, head) { + if (ior->type == type && (id < 0 || ior->id == id)) + return ior; + } + return NULL; +} + +void +nvkm_ior_del(struct nvkm_ior **pior) +{ + struct nvkm_ior *ior = *pior; + if (ior) { + IOR_DBG(ior, "dtor"); + list_del(&ior->head); + kfree(*pior); + *pior = NULL; + } +} + +int +nvkm_ior_new_(const struct nvkm_ior_func *func, struct nvkm_disp *disp, + enum nvkm_ior_type type, int id) +{ + struct nvkm_ior *ior; + if (!(ior = kzalloc(sizeof(*ior), GFP_KERNEL))) + return -ENOMEM; + ior->func = func; + ior->disp = disp; + ior->type = type; + ior->id = id; + snprintf(ior->name, sizeof(ior->name), "%s-%d", + nvkm_ior_name[ior->type], ior->id); + list_add_tail(&ior->head, &disp->ior); + IOR_DBG(ior, "ctor"); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h new file mode 100644 index 000000000000..a24312fb0228 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h @@ -0,0 +1,169 @@ +#ifndef __NVKM_DISP_IOR_H__ +#define __NVKM_DISP_IOR_H__ +#include "priv.h" +struct nvkm_i2c_aux; + +struct nvkm_ior { + const struct nvkm_ior_func *func; + struct nvkm_disp *disp; + enum nvkm_ior_type { + DAC, + SOR, + PIOR, + } type; + int id; + char name[8]; + + struct list_head head; + + struct nvkm_ior_state { + struct nvkm_outp *outp; + unsigned rgdiv; + unsigned proto_evo:4; + enum nvkm_ior_proto { + CRT, + TMDS, + LVDS, + DP, + UNKNOWN + } proto:3; + unsigned link:2; + unsigned head:4; + } arm, asy; + + /* Armed DP state. */ + struct { + bool mst; + bool ef; + u8 nr; + u8 bw; + } dp; +}; + +struct nvkm_ior_func { + struct { + int (*get)(struct nvkm_outp *, int *link); + void (*set)(struct nvkm_outp *, struct nvkm_ior *); + } route; + + void (*state)(struct nvkm_ior *, struct nvkm_ior_state *); + void (*power)(struct nvkm_ior *, bool normal, bool pu, + bool data, bool vsync, bool hsync); + int (*sense)(struct nvkm_ior *, u32 loadval); + void (*clock)(struct nvkm_ior *); + void (*war_2)(struct nvkm_ior *); + void (*war_3)(struct nvkm_ior *); + + struct { + void (*ctrl)(struct nvkm_ior *, int head, bool enable, + u8 max_ac_packet, u8 rekey, u8 *avi, u8 avi_size, + u8 *vendor, u8 vendor_size); + } hdmi; + + struct { + u8 lanes[4]; + int (*links)(struct nvkm_ior *, struct nvkm_i2c_aux *); + void (*power)(struct nvkm_ior *, int nr); + void (*pattern)(struct nvkm_ior *, int pattern); + void (*drive)(struct nvkm_ior *, int ln, int pc, + int dc, int pe, int tx_pu); + void (*vcpi)(struct nvkm_ior *, int head, u8 slot, + u8 slot_nr, u16 pbn, u16 aligned); + void (*audio)(struct nvkm_ior *, int head, bool enable); + void (*audio_sym)(struct nvkm_ior *, int head, u16 h, u32 v); + void (*activesym)(struct nvkm_ior *, int head, + u8 TU, u8 VTUa, u8 VTUf, u8 VTUi); + void (*watermark)(struct nvkm_ior *, int head, u8 watermark); + } dp; + + struct { + void (*hpd)(struct nvkm_ior *, int head, bool present); + void (*eld)(struct nvkm_ior *, u8 *data, u8 size); + } hda; +}; + +int nvkm_ior_new_(const struct nvkm_ior_func *func, struct nvkm_disp *, + enum nvkm_ior_type type, int id); +void nvkm_ior_del(struct nvkm_ior **); +struct nvkm_ior *nvkm_ior_find(struct nvkm_disp *, enum nvkm_ior_type, int id); + +static inline u32 +nv50_ior_base(struct nvkm_ior *ior) +{ + return ior->id * 0x800; +} + +void nv50_dac_power(struct nvkm_ior *, bool, bool, bool, bool, bool); +int nv50_dac_sense(struct nvkm_ior *, u32); + +void nv50_pior_depth(struct nvkm_ior *, struct nvkm_ior_state *, u32 ctrl); + +static inline u32 +nv50_sor_link(struct nvkm_ior *ior) +{ + return nv50_ior_base(ior) + ((ior->asy.link == 2) * 0x80); +} + +int nv50_sor_new_(const struct nvkm_ior_func *, struct nvkm_disp *, int id); +void nv50_sor_state(struct nvkm_ior *, struct nvkm_ior_state *); +void nv50_sor_power(struct nvkm_ior *, bool, bool, bool, bool, bool); +void nv50_sor_clock(struct nvkm_ior *); + +void g94_sor_state(struct nvkm_ior *, struct nvkm_ior_state *); +int g94_sor_dp_links(struct nvkm_ior *, struct nvkm_i2c_aux *); +void g94_sor_dp_power(struct nvkm_ior *, int); +void g94_sor_dp_pattern(struct nvkm_ior *, int); +void g94_sor_dp_drive(struct nvkm_ior *, int, int, int, int, int); +void g94_sor_dp_audio_sym(struct nvkm_ior *, int, u16, u32); +void g94_sor_dp_activesym(struct nvkm_ior *, int, u8, u8, u8, u8); +void g94_sor_dp_watermark(struct nvkm_ior *, int, u8); + +void gt215_sor_dp_audio(struct nvkm_ior *, int, bool); + +int gf119_sor_new_(const struct nvkm_ior_func *, struct nvkm_disp *, int id); +void gf119_sor_state(struct nvkm_ior *, struct nvkm_ior_state *); +void gf119_sor_clock(struct nvkm_ior *); +int gf119_sor_dp_links(struct nvkm_ior *, struct nvkm_i2c_aux *); +void gf119_sor_dp_pattern(struct nvkm_ior *, int); +void gf119_sor_dp_drive(struct nvkm_ior *, int, int, int, int, int); +void gf119_sor_dp_vcpi(struct nvkm_ior *, int, u8, u8, u16, u16); +void gf119_sor_dp_audio(struct nvkm_ior *, int, bool); +void gf119_sor_dp_audio_sym(struct nvkm_ior *, int, u16, u32); +void gf119_sor_dp_watermark(struct nvkm_ior *, int, u8); + +void gm107_sor_dp_pattern(struct nvkm_ior *, int); + +void g84_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8); +void gt215_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8); +void gf119_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8); +void gk104_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8); + +void gt215_hda_hpd(struct nvkm_ior *, int, bool); +void gt215_hda_eld(struct nvkm_ior *, u8 *, u8); + +void gf119_hda_hpd(struct nvkm_ior *, int, bool); +void gf119_hda_eld(struct nvkm_ior *, u8 *, u8); + +#define IOR_MSG(i,l,f,a...) do { \ + struct nvkm_ior *_ior = (i); \ + nvkm_##l(&_ior->disp->engine.subdev, "%s: "f, _ior->name, ##a); \ +} while(0) +#define IOR_WARN(i,f,a...) IOR_MSG((i), warn, f, ##a) +#define IOR_DBG(i,f,a...) IOR_MSG((i), debug, f, ##a) + +int nv50_dac_new(struct nvkm_disp *, int); +int gf119_dac_new(struct nvkm_disp *, int); + +int nv50_pior_new(struct nvkm_disp *, int); + +int nv50_sor_new(struct nvkm_disp *, int); +int g84_sor_new(struct nvkm_disp *, int); +int g94_sor_new(struct nvkm_disp *, int); +int mcp77_sor_new(struct nvkm_disp *, int); +int gt215_sor_new(struct nvkm_disp *, int); +int mcp89_sor_new(struct nvkm_disp *, int); +int gf119_sor_new(struct nvkm_disp *, int); +int gk104_sor_new(struct nvkm_disp *, int); +int gm107_sor_new(struct nvkm_disp *, int); +int gm200_sor_new(struct nvkm_disp *, int); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp77.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp77.c new file mode 100644 index 000000000000..d7e0fbb12bf1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp77.c @@ -0,0 +1,43 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * 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 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 HOLDER(S) OR AUTHOR(S) 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 "nv50.h" +#include "head.h" +#include "ior.h" +#include "rootnv50.h" + +static const struct nv50_disp_func +mcp77_disp = { + .intr = nv50_disp_intr, + .uevent = &nv50_disp_chan_uevent, + .super = nv50_disp_super, + .root = &g94_disp_root_oclass, + .head.new = nv50_head_new, + .dac = { .nr = 3, .new = nv50_dac_new }, + .sor = { .nr = 4, .new = mcp77_sor_new }, + .pior = { .nr = 3, .new = nv50_pior_new }, +}; + +int +mcp77_disp_new(struct nvkm_device *device, int index, struct nvkm_disp **pdisp) +{ + return nv50_disp_new_(&mcp77_disp, device, index, 2, pdisp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c new file mode 100644 index 000000000000..7b75c57c12ed --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c @@ -0,0 +1,43 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * 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 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 HOLDER(S) OR AUTHOR(S) 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 "nv50.h" +#include "head.h" +#include "ior.h" +#include "rootnv50.h" + +static const struct nv50_disp_func +mcp89_disp = { + .intr = nv50_disp_intr, + .uevent = &nv50_disp_chan_uevent, + .super = nv50_disp_super, + .root = >215_disp_root_oclass, + .head.new = nv50_head_new, + .dac = { .nr = 3, .new = nv50_dac_new }, + .sor = { .nr = 4, .new = mcp89_sor_new }, + .pior = { .nr = 3, .new = nv50_pior_new }, +}; + +int +mcp89_disp_new(struct nvkm_device *device, int index, struct nvkm_disp **pdisp) +{ + return nv50_disp_new_(&mcp89_disp, device, index, 2, pdisp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv04.c index 67254ce6f83f..b780ba1a3bc7 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv04.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv04.c @@ -22,6 +22,7 @@ * Authors: Ben Skeggs */ #include "priv.h" +#include "head.h" static const struct nvkm_disp_oclass * nv04_disp_root(struct nvkm_disp *disp) @@ -30,20 +31,6 @@ nv04_disp_root(struct nvkm_disp *disp) } static void -nv04_disp_vblank_init(struct nvkm_disp *disp, int head) -{ - struct nvkm_device *device = disp->engine.subdev.device; - nvkm_wr32(device, 0x600140 + (head * 0x2000) , 0x00000001); -} - -static void -nv04_disp_vblank_fini(struct nvkm_disp *disp, int head) -{ - struct nvkm_device *device = disp->engine.subdev.device; - nvkm_wr32(device, 0x600140 + (head * 0x2000) , 0x00000000); -} - -static void nv04_disp_intr(struct nvkm_disp *disp) { struct nvkm_subdev *subdev = &disp->engine.subdev; @@ -74,12 +61,22 @@ static const struct nvkm_disp_func nv04_disp = { .intr = nv04_disp_intr, .root = nv04_disp_root, - .head.vblank_init = nv04_disp_vblank_init, - .head.vblank_fini = nv04_disp_vblank_fini, }; int nv04_disp_new(struct nvkm_device *device, int index, struct nvkm_disp **pdisp) { - return nvkm_disp_new_(&nv04_disp, device, index, 2, pdisp); + int ret, i; + + ret = nvkm_disp_new_(&nv04_disp, device, index, pdisp); + if (ret) + return ret; + + for (i = 0; i < 2; i++) { + ret = nv04_head_new(*pdisp, i); + if (ret) + return ret; + } + + return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c index 0db8efbf1c2e..0c570dbd3021 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c @@ -22,6 +22,8 @@ * Authors: Ben Skeggs */ #include "nv50.h" +#include "head.h" +#include "ior.h" #include "rootnv50.h" #include <core/client.h> @@ -40,77 +42,6 @@ nv50_disp_root_(struct nvkm_disp *base) return nv50_disp(base)->func->root; } -static int -nv50_disp_outp_internal_crt_(struct nvkm_disp *base, int index, - struct dcb_output *dcb, struct nvkm_output **poutp) -{ - struct nv50_disp *disp = nv50_disp(base); - return disp->func->outp.internal.crt(base, index, dcb, poutp); -} - -static int -nv50_disp_outp_internal_tmds_(struct nvkm_disp *base, int index, - struct dcb_output *dcb, - struct nvkm_output **poutp) -{ - struct nv50_disp *disp = nv50_disp(base); - return disp->func->outp.internal.tmds(base, index, dcb, poutp); -} - -static int -nv50_disp_outp_internal_lvds_(struct nvkm_disp *base, int index, - struct dcb_output *dcb, - struct nvkm_output **poutp) -{ - struct nv50_disp *disp = nv50_disp(base); - return disp->func->outp.internal.lvds(base, index, dcb, poutp); -} - -static int -nv50_disp_outp_internal_dp_(struct nvkm_disp *base, int index, - struct dcb_output *dcb, struct nvkm_output **poutp) -{ - struct nv50_disp *disp = nv50_disp(base); - if (disp->func->outp.internal.dp) - return disp->func->outp.internal.dp(base, index, dcb, poutp); - return -ENODEV; -} - -static int -nv50_disp_outp_external_tmds_(struct nvkm_disp *base, int index, - struct dcb_output *dcb, - struct nvkm_output **poutp) -{ - struct nv50_disp *disp = nv50_disp(base); - if (disp->func->outp.external.tmds) - return disp->func->outp.external.tmds(base, index, dcb, poutp); - return -ENODEV; -} - -static int -nv50_disp_outp_external_dp_(struct nvkm_disp *base, int index, - struct dcb_output *dcb, struct nvkm_output **poutp) -{ - struct nv50_disp *disp = nv50_disp(base); - if (disp->func->outp.external.dp) - return disp->func->outp.external.dp(base, index, dcb, poutp); - return -ENODEV; -} - -static void -nv50_disp_vblank_fini_(struct nvkm_disp *base, int head) -{ - struct nv50_disp *disp = nv50_disp(base); - disp->func->head.vblank_fini(disp, head); -} - -static void -nv50_disp_vblank_init_(struct nvkm_disp *base, int head) -{ - struct nv50_disp *disp = nv50_disp(base); - disp->func->head.vblank_init(disp, head); -} - static void nv50_disp_intr_(struct nvkm_disp *base) { @@ -123,6 +54,8 @@ nv50_disp_dtor_(struct nvkm_disp *base) { struct nv50_disp *disp = nv50_disp(base); nvkm_event_fini(&disp->uevent); + if (disp->wq) + destroy_workqueue(disp->wq); return disp; } @@ -131,14 +64,6 @@ nv50_disp_ = { .dtor = nv50_disp_dtor_, .intr = nv50_disp_intr_, .root = nv50_disp_root_, - .outp.internal.crt = nv50_disp_outp_internal_crt_, - .outp.internal.tmds = nv50_disp_outp_internal_tmds_, - .outp.internal.lvds = nv50_disp_outp_internal_lvds_, - .outp.internal.dp = nv50_disp_outp_internal_dp_, - .outp.external.tmds = nv50_disp_outp_external_tmds_, - .outp.external.dp = nv50_disp_outp_external_dp_, - .head.vblank_init = nv50_disp_vblank_init_, - .head.vblank_fini = nv50_disp_vblank_fini_, }; int @@ -146,517 +71,227 @@ nv50_disp_new_(const struct nv50_disp_func *func, struct nvkm_device *device, int index, int heads, struct nvkm_disp **pdisp) { struct nv50_disp *disp; - int ret; + int ret, i; if (!(disp = kzalloc(sizeof(*disp), GFP_KERNEL))) return -ENOMEM; - INIT_WORK(&disp->supervisor, func->super); disp->func = func; *pdisp = &disp->base; - ret = nvkm_disp_ctor(&nv50_disp_, device, index, heads, &disp->base); + ret = nvkm_disp_ctor(&nv50_disp_, device, index, &disp->base); if (ret) return ret; - return nvkm_event_init(func->uevent, 1, 1 + (heads * 4), &disp->uevent); -} - -void -nv50_disp_vblank_fini(struct nv50_disp *disp, int head) -{ - struct nvkm_device *device = disp->base.engine.subdev.device; - nvkm_mask(device, 0x61002c, (4 << head), 0); -} - -void -nv50_disp_vblank_init(struct nv50_disp *disp, int head) -{ - struct nvkm_device *device = disp->base.engine.subdev.device; - nvkm_mask(device, 0x61002c, (4 << head), (4 << head)); -} - -static const struct nvkm_enum -nv50_disp_intr_error_type[] = { - { 3, "ILLEGAL_MTHD" }, - { 4, "INVALID_VALUE" }, - { 5, "INVALID_STATE" }, - { 7, "INVALID_HANDLE" }, - {} -}; - -static const struct nvkm_enum -nv50_disp_intr_error_code[] = { - { 0x00, "" }, - {} -}; - -static void -nv50_disp_intr_error(struct nv50_disp *disp, int chid) -{ - struct nvkm_subdev *subdev = &disp->base.engine.subdev; - struct nvkm_device *device = subdev->device; - u32 data = nvkm_rd32(device, 0x610084 + (chid * 0x08)); - u32 addr = nvkm_rd32(device, 0x610080 + (chid * 0x08)); - u32 code = (addr & 0x00ff0000) >> 16; - u32 type = (addr & 0x00007000) >> 12; - u32 mthd = (addr & 0x00000ffc); - const struct nvkm_enum *ec, *et; - - et = nvkm_enum_find(nv50_disp_intr_error_type, type); - ec = nvkm_enum_find(nv50_disp_intr_error_code, code); - - nvkm_error(subdev, - "ERROR %d [%s] %02x [%s] chid %d mthd %04x data %08x\n", - type, et ? et->name : "", code, ec ? ec->name : "", - chid, mthd, data); - - if (chid < ARRAY_SIZE(disp->chan)) { - switch (mthd) { - case 0x0080: - nv50_disp_chan_mthd(disp->chan[chid], NV_DBG_ERROR); - break; - default: - break; - } - } - - nvkm_wr32(device, 0x610020, 0x00010000 << chid); - nvkm_wr32(device, 0x610080 + (chid * 0x08), 0x90000000); -} - -static struct nvkm_output * -exec_lookup(struct nv50_disp *disp, int head, int or, u32 ctrl, - u32 *data, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, - struct nvbios_outp *info) -{ - struct nvkm_subdev *subdev = &disp->base.engine.subdev; - struct nvkm_bios *bios = subdev->device->bios; - struct nvkm_output *outp; - u16 mask, type; - - if (or < 4) { - type = DCB_OUTPUT_ANALOG; - mask = 0; - } else - if (or < 8) { - switch (ctrl & 0x00000f00) { - case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break; - case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break; - case 0x00000200: type = DCB_OUTPUT_TMDS; mask = 2; break; - case 0x00000500: type = DCB_OUTPUT_TMDS; mask = 3; break; - case 0x00000800: type = DCB_OUTPUT_DP; mask = 1; break; - case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break; - default: - nvkm_error(subdev, "unknown SOR mc %08x\n", ctrl); - return NULL; - } - or -= 4; - } else { - or = or - 8; - type = 0x0010; - mask = 0; - switch (ctrl & 0x00000f00) { - case 0x00000000: type |= disp->pior.type[or]; break; - default: - nvkm_error(subdev, "unknown PIOR mc %08x\n", ctrl); - return NULL; - } - } + disp->wq = create_singlethread_workqueue("nvkm-disp"); + if (!disp->wq) + return -ENOMEM; + INIT_WORK(&disp->supervisor, func->super); - mask = 0x00c0 & (mask << 6); - mask |= 0x0001 << or; - mask |= 0x0100 << head; - - list_for_each_entry(outp, &disp->base.outp, head) { - if ((outp->info.hasht & 0xff) == type && - (outp->info.hashm & mask) == mask) { - *data = nvbios_outp_match(bios, outp->info.hasht, mask, - ver, hdr, cnt, len, info); - if (!*data) - return NULL; - return outp; - } + for (i = 0; func->head.new && i < heads; i++) { + ret = func->head.new(&disp->base, i); + if (ret) + return ret; } - return NULL; -} - -static struct nvkm_output * -exec_script(struct nv50_disp *disp, int head, int id) -{ - struct nvkm_subdev *subdev = &disp->base.engine.subdev; - struct nvkm_device *device = subdev->device; - struct nvkm_bios *bios = device->bios; - struct nvkm_output *outp; - struct nvbios_outp info; - u8 ver, hdr, cnt, len; - u32 data, ctrl = 0; - u32 reg; - int i; - - /* DAC */ - for (i = 0; !(ctrl & (1 << head)) && i < disp->func->dac.nr; i++) - ctrl = nvkm_rd32(device, 0x610b5c + (i * 8)); - - /* SOR */ - if (!(ctrl & (1 << head))) { - if (device->chipset < 0x90 || - device->chipset == 0x92 || - device->chipset == 0xa0) { - reg = 0x610b74; - } else { - reg = 0x610798; - } - for (i = 0; !(ctrl & (1 << head)) && i < disp->func->sor.nr; i++) - ctrl = nvkm_rd32(device, reg + (i * 8)); - i += 4; + for (i = 0; func->dac.new && i < func->dac.nr; i++) { + ret = func->dac.new(&disp->base, i); + if (ret) + return ret; } - /* PIOR */ - if (!(ctrl & (1 << head))) { - for (i = 0; !(ctrl & (1 << head)) && i < disp->func->pior.nr; i++) - ctrl = nvkm_rd32(device, 0x610b84 + (i * 8)); - i += 8; + for (i = 0; func->pior.new && i < func->pior.nr; i++) { + ret = func->pior.new(&disp->base, i); + if (ret) + return ret; } - if (!(ctrl & (1 << head))) - return NULL; - i--; - - outp = exec_lookup(disp, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info); - if (outp) { - struct nvbios_init init = { - .subdev = subdev, - .bios = bios, - .offset = info.script[id], - .outp = &outp->info, - .crtc = head, - .execute = 1, - }; - - nvbios_exec(&init); + for (i = 0; func->sor.new && i < func->sor.nr; i++) { + ret = func->sor.new(&disp->base, i); + if (ret) + return ret; } - return outp; + return nvkm_event_init(func->uevent, 1, 1 + (heads * 4), &disp->uevent); } -static struct nvkm_output * -exec_clkcmp(struct nv50_disp *disp, int head, int id, u32 pclk, u32 *conf) +static u32 +nv50_disp_super_iedt(struct nvkm_head *head, struct nvkm_outp *outp, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_outp *iedt) { - struct nvkm_subdev *subdev = &disp->base.engine.subdev; - struct nvkm_device *device = subdev->device; - struct nvkm_bios *bios = device->bios; - struct nvkm_output *outp; - struct nvbios_outp info1; - struct nvbios_ocfg info2; - u8 ver, hdr, cnt, len; - u32 data, ctrl = 0; - u32 reg; - int i; - - /* DAC */ - for (i = 0; !(ctrl & (1 << head)) && i < disp->func->dac.nr; i++) - ctrl = nvkm_rd32(device, 0x610b58 + (i * 8)); - - /* SOR */ - if (!(ctrl & (1 << head))) { - if (device->chipset < 0x90 || - device->chipset == 0x92 || - device->chipset == 0xa0) { - reg = 0x610b70; - } else { - reg = 0x610794; - } - for (i = 0; !(ctrl & (1 << head)) && i < disp->func->sor.nr; i++) - ctrl = nvkm_rd32(device, reg + (i * 8)); - i += 4; - } - - /* PIOR */ - if (!(ctrl & (1 << head))) { - for (i = 0; !(ctrl & (1 << head)) && i < disp->func->pior.nr; i++) - ctrl = nvkm_rd32(device, 0x610b80 + (i * 8)); - i += 8; - } - - if (!(ctrl & (1 << head))) - return NULL; - i--; - - outp = exec_lookup(disp, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info1); - if (!outp) - return NULL; - - *conf = (ctrl & 0x00000f00) >> 8; - if (outp->info.location == 0) { - switch (outp->info.type) { - case DCB_OUTPUT_TMDS: - if (*conf == 5) - *conf |= 0x0100; - break; - case DCB_OUTPUT_LVDS: - *conf |= disp->sor.lvdsconf; - break; - default: - break; - } - } else { - *conf = (ctrl & 0x00000f00) >> 8; - pclk = pclk / 2; - } - - data = nvbios_ocfg_match(bios, data, *conf & 0xff, *conf >> 8, - &ver, &hdr, &cnt, &len, &info2); - if (data && id < 0xff) { - data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk); - if (data) { - struct nvbios_init init = { - .subdev = subdev, - .bios = bios, - .offset = data, - .outp = &outp->info, - .crtc = head, - .execute = 1, - }; - - nvbios_exec(&init); - } - } - - return outp; + struct nvkm_bios *bios = head->disp->engine.subdev.device->bios; + const u8 l = ffs(outp->info.link); + const u16 t = outp->info.hasht; + const u16 m = (0x0100 << head->id) | (l << 6) | outp->info.or; + u32 data = nvbios_outp_match(bios, t, m, ver, hdr, cnt, len, iedt); + if (!data) + OUTP_DBG(outp, "missing IEDT for %04x:%04x", t, m); + return data; } -static bool -nv50_disp_dptmds_war(struct nvkm_device *device) +static void +nv50_disp_super_ied_on(struct nvkm_head *head, + struct nvkm_ior *ior, int id, u32 khz) { - switch (device->chipset) { - case 0x94: - case 0x96: - case 0x98: - return true; - default: - break; + struct nvkm_subdev *subdev = &head->disp->engine.subdev; + struct nvkm_bios *bios = subdev->device->bios; + struct nvkm_outp *outp = ior->asy.outp; + struct nvbios_ocfg iedtrs; + struct nvbios_outp iedt; + u8 ver, hdr, cnt, len, flags = 0x00; + u32 data; + + if (!outp) { + IOR_DBG(ior, "nothing to attach"); + return; } - return false; -} -static bool -nv50_disp_dptmds_war_needed(struct nv50_disp *disp, struct dcb_output *outp) -{ - struct nvkm_device *device = disp->base.engine.subdev.device; - const u32 soff = __ffs(outp->or) * 0x800; - if (nv50_disp_dptmds_war(device) && outp->type == DCB_OUTPUT_TMDS) { - switch (nvkm_rd32(device, 0x614300 + soff) & 0x00030000) { - case 0x00000000: - case 0x00030000: - return true; - default: - break; + /* Lookup IED table for the device. */ + data = nv50_disp_super_iedt(head, outp, &ver, &hdr, &cnt, &len, &iedt); + if (!data) + return; + + /* Lookup IEDT runtime settings for the current configuration. */ + if (ior->type == SOR) { + if (ior->asy.proto == LVDS) { + if (head->asy.or.depth == 24) + flags |= 0x02; } + if (ior->asy.link == 3) + flags |= 0x01; } - return false; - -} - -static void -nv50_disp_dptmds_war_2(struct nv50_disp *disp, struct dcb_output *outp) -{ - struct nvkm_device *device = disp->base.engine.subdev.device; - const u32 soff = __ffs(outp->or) * 0x800; - if (!nv50_disp_dptmds_war_needed(disp, outp)) + data = nvbios_ocfg_match(bios, data, ior->asy.proto_evo, flags, + &ver, &hdr, &cnt, &len, &iedtrs); + if (!data) { + OUTP_DBG(outp, "missing IEDT RS for %02x:%02x", + ior->asy.proto_evo, flags); return; - - nvkm_mask(device, 0x00e840, 0x80000000, 0x80000000); - nvkm_mask(device, 0x614300 + soff, 0x03000000, 0x03000000); - nvkm_mask(device, 0x61c10c + soff, 0x00000001, 0x00000001); - - nvkm_mask(device, 0x61c00c + soff, 0x0f000000, 0x00000000); - nvkm_mask(device, 0x61c008 + soff, 0xff000000, 0x14000000); - nvkm_usec(device, 400, NVKM_DELAY); - nvkm_mask(device, 0x61c008 + soff, 0xff000000, 0x00000000); - nvkm_mask(device, 0x61c00c + soff, 0x0f000000, 0x01000000); - - if (nvkm_rd32(device, 0x61c004 + soff) & 0x00000001) { - u32 seqctl = nvkm_rd32(device, 0x61c030 + soff); - u32 pu_pc = seqctl & 0x0000000f; - nvkm_wr32(device, 0x61c040 + soff + pu_pc * 4, 0x1f008000); } -} -static void -nv50_disp_dptmds_war_3(struct nv50_disp *disp, struct dcb_output *outp) -{ - struct nvkm_device *device = disp->base.engine.subdev.device; - const u32 soff = __ffs(outp->or) * 0x800; - u32 sorpwr; - - if (!nv50_disp_dptmds_war_needed(disp, outp)) + /* Execute the OnInt[23] script for the current frequency. */ + data = nvbios_oclk_match(bios, iedtrs.clkcmp[id], khz); + if (!data) { + OUTP_DBG(outp, "missing IEDT RSS %d for %02x:%02x %d khz", + id, ior->asy.proto_evo, flags, khz); return; - - sorpwr = nvkm_rd32(device, 0x61c004 + soff); - if (sorpwr & 0x00000001) { - u32 seqctl = nvkm_rd32(device, 0x61c030 + soff); - u32 pd_pc = (seqctl & 0x00000f00) >> 8; - u32 pu_pc = seqctl & 0x0000000f; - - nvkm_wr32(device, 0x61c040 + soff + pd_pc * 4, 0x1f008000); - - nvkm_msec(device, 2000, - if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000)) - break; - ); - nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000000); - nvkm_msec(device, 2000, - if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000)) - break; - ); - - nvkm_wr32(device, 0x61c040 + soff + pd_pc * 4, 0x00002000); - nvkm_wr32(device, 0x61c040 + soff + pu_pc * 4, 0x1f000000); } - nvkm_mask(device, 0x61c10c + soff, 0x00000001, 0x00000000); - nvkm_mask(device, 0x614300 + soff, 0x03000000, 0x00000000); - - if (sorpwr & 0x00000001) { - nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000001); - } + nvbios_init(subdev, data, + init.outp = &outp->info; + init.or = ior->id; + init.link = ior->asy.link; + init.head = head->id; + ); } static void -nv50_disp_update_sppll1(struct nv50_disp *disp) +nv50_disp_super_ied_off(struct nvkm_head *head, struct nvkm_ior *ior, int id) { - struct nvkm_device *device = disp->base.engine.subdev.device; - bool used = false; - int sor; + struct nvkm_outp *outp = ior->arm.outp; + struct nvbios_outp iedt; + u8 ver, hdr, cnt, len; + u32 data; - if (!nv50_disp_dptmds_war(device)) + if (!outp) { + IOR_DBG(ior, "nothing attached"); return; - - for (sor = 0; sor < disp->func->sor.nr; sor++) { - u32 clksor = nvkm_rd32(device, 0x614300 + (sor * 0x800)); - switch (clksor & 0x03000000) { - case 0x02000000: - case 0x03000000: - used = true; - break; - default: - break; - } } - if (used) + data = nv50_disp_super_iedt(head, outp, &ver, &hdr, &cnt, &len, &iedt); + if (!data) return; - nvkm_mask(device, 0x00e840, 0x80000000, 0x00000000); + nvbios_init(&head->disp->engine.subdev, iedt.script[id], + init.outp = &outp->info; + init.or = ior->id; + init.link = ior->arm.link; + init.head = head->id; + ); } -static void -nv50_disp_intr_unk10_0(struct nv50_disp *disp, int head) +static struct nvkm_ior * +nv50_disp_super_ior_asy(struct nvkm_head *head) { - exec_script(disp, head, 1); + struct nvkm_ior *ior; + list_for_each_entry(ior, &head->disp->ior, head) { + if (ior->asy.head & (1 << head->id)) { + HEAD_DBG(head, "to %s", ior->name); + return ior; + } + } + HEAD_DBG(head, "nothing to attach"); + return NULL; } -static void -nv50_disp_intr_unk20_0(struct nv50_disp *disp, int head) +static struct nvkm_ior * +nv50_disp_super_ior_arm(struct nvkm_head *head) { - struct nvkm_subdev *subdev = &disp->base.engine.subdev; - struct nvkm_output *outp = exec_script(disp, head, 2); - - /* the binary driver does this outside of the supervisor handling - * (after the third supervisor from a detach). we (currently?) - * allow both detach/attach to happen in the same set of - * supervisor interrupts, so it would make sense to execute this - * (full power down?) script after all the detach phases of the - * supervisor handling. like with training if needed from the - * second supervisor, nvidia doesn't do this, so who knows if it's - * entirely safe, but it does appear to work.. - * - * without this script being run, on some configurations i've - * seen, switching from DP to TMDS on a DP connector may result - * in a blank screen (SOR_PWR off/on can restore it) - */ - if (outp && outp->info.type == DCB_OUTPUT_DP) { - struct nvkm_output_dp *outpdp = nvkm_output_dp(outp); - struct nvbios_init init = { - .subdev = subdev, - .bios = subdev->device->bios, - .outp = &outp->info, - .crtc = head, - .offset = outpdp->info.script[4], - .execute = 1, - }; - - nvkm_notify_put(&outpdp->irq); - nvbios_exec(&init); - atomic_set(&outpdp->lt.done, 0); + struct nvkm_ior *ior; + list_for_each_entry(ior, &head->disp->ior, head) { + if (ior->arm.head & (1 << head->id)) { + HEAD_DBG(head, "on %s", ior->name); + return ior; + } } + HEAD_DBG(head, "nothing attached"); + return NULL; } -static void -nv50_disp_intr_unk20_1(struct nv50_disp *disp, int head) +void +nv50_disp_super_3_0(struct nv50_disp *disp, struct nvkm_head *head) { - struct nvkm_device *device = disp->base.engine.subdev.device; - struct nvkm_devinit *devinit = device->devinit; - u32 pclk = nvkm_rd32(device, 0x610ad0 + (head * 0x540)) & 0x3fffff; - if (pclk) - nvkm_devinit_pll_set(devinit, PLL_VPLL0 + head, pclk); + struct nvkm_ior *ior; + + /* Determine which OR, if any, we're attaching to the head. */ + HEAD_DBG(head, "supervisor 3.0"); + ior = nv50_disp_super_ior_asy(head); + if (!ior) + return; + + /* Execute OnInt3 IED script. */ + nv50_disp_super_ied_on(head, ior, 1, head->asy.hz / 1000); + + /* OR-specific handling. */ + if (ior->func->war_3) + ior->func->war_3(ior); } static void -nv50_disp_intr_unk20_2_dp(struct nv50_disp *disp, int head, - struct dcb_output *outp, u32 pclk) +nv50_disp_super_2_2_dp(struct nvkm_head *head, struct nvkm_ior *ior) { - struct nvkm_subdev *subdev = &disp->base.engine.subdev; - struct nvkm_device *device = subdev->device; - const int link = !(outp->sorconf.link & 1); - const int or = ffs(outp->or) - 1; - const u32 soff = ( or * 0x800); - const u32 loff = (link * 0x080) + soff; - const u32 ctrl = nvkm_rd32(device, 0x610794 + (or * 8)); - const u32 symbol = 100000; - const s32 vactive = nvkm_rd32(device, 0x610af8 + (head * 0x540)) & 0xffff; - const s32 vblanke = nvkm_rd32(device, 0x610ae8 + (head * 0x540)) & 0xffff; - const s32 vblanks = nvkm_rd32(device, 0x610af0 + (head * 0x540)) & 0xffff; - u32 dpctrl = nvkm_rd32(device, 0x61c10c + loff); - u32 clksor = nvkm_rd32(device, 0x614300 + soff); + struct nvkm_subdev *subdev = &head->disp->engine.subdev; + const u32 khz = head->asy.hz / 1000; + const u32 linkKBps = ior->dp.bw * 27000; + const u32 symbol = 100000; int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0; int TU, VTUi, VTUf, VTUa; u64 link_data_rate, link_ratio, unk; u32 best_diff = 64 * symbol; - u32 link_nr, link_bw, bits; - u64 value; - - link_bw = (clksor & 0x000c0000) ? 270000 : 162000; - link_nr = hweight32(dpctrl & 0x000f0000); + u64 h, v; /* symbols/hblank - algorithm taken from comments in tegra driver */ - value = vblanke + vactive - vblanks - 7; - value = value * link_bw; - do_div(value, pclk); - value = value - (3 * !!(dpctrl & 0x00004000)) - (12 / link_nr); - nvkm_mask(device, 0x61c1e8 + soff, 0x0000ffff, value); + h = head->asy.hblanke + head->asy.htotal - head->asy.hblanks - 7; + h = h * linkKBps; + do_div(h, khz); + h = h - (3 * ior->dp.ef) - (12 / ior->dp.nr); /* symbols/vblank - algorithm taken from comments in tegra driver */ - value = vblanks - vblanke - 25; - value = value * link_bw; - do_div(value, pclk); - value = value - ((36 / link_nr) + 3) - 1; - nvkm_mask(device, 0x61c1ec + soff, 0x00ffffff, value); + v = head->asy.vblanks - head->asy.vblanke - 25; + v = v * linkKBps; + do_div(v, khz); + v = v - ((36 / ior->dp.nr) + 3) - 1; - /* watermark / activesym */ - if ((ctrl & 0xf0000) == 0x60000) bits = 30; - else if ((ctrl & 0xf0000) == 0x50000) bits = 24; - else bits = 18; + ior->func->dp.audio_sym(ior, head->id, h, v); - link_data_rate = (pclk * bits / 8) / link_nr; + /* watermark / activesym */ + link_data_rate = (khz * head->asy.or.depth / 8) / ior->dp.nr; /* calculate ratio of packed data rate to link symbol rate */ link_ratio = link_data_rate * symbol; - do_div(link_ratio, link_bw); + do_div(link_ratio, linkKBps); - for (TU = 64; TU >= 32; TU--) { + for (TU = 64; ior->func->dp.activesym && TU >= 32; TU--) { /* calculate average number of valid symbols in each TU */ u32 tu_valid = link_ratio * TU; u32 calc, diff; @@ -707,9 +342,15 @@ nv50_disp_intr_unk20_2_dp(struct nv50_disp *disp, int head, } } - if (!bestTU) { - nvkm_error(subdev, "unable to find suitable dp config\n"); - return; + if (ior->func->dp.activesym) { + if (!bestTU) { + nvkm_error(subdev, "unable to determine dp config\n"); + return; + } + ior->func->dp.activesym(ior, head->id, bestTU, + bestVTUa, bestVTUf, bestVTUi); + } else { + bestTU = 64; } /* XXX close to vbios numbers, but not right */ @@ -719,190 +360,223 @@ nv50_disp_intr_unk20_2_dp(struct nv50_disp *disp, int head, do_div(unk, symbol); unk += 6; - nvkm_mask(device, 0x61c10c + loff, 0x000001fc, bestTU << 2); - nvkm_mask(device, 0x61c128 + loff, 0x010f7f3f, bestVTUa << 24 | - bestVTUf << 16 | - bestVTUi << 8 | unk); + ior->func->dp.watermark(ior, head->id, unk); } -static void -nv50_disp_intr_unk20_2(struct nv50_disp *disp, int head) +void +nv50_disp_super_2_2(struct nv50_disp *disp, struct nvkm_head *head) { - struct nvkm_device *device = disp->base.engine.subdev.device; - struct nvkm_output *outp; - u32 pclk = nvkm_rd32(device, 0x610ad0 + (head * 0x540)) & 0x3fffff; - u32 hval, hreg = 0x614200 + (head * 0x800); - u32 oval, oreg; - u32 mask, conf; - - outp = exec_clkcmp(disp, head, 0xff, pclk, &conf); - if (!outp) + const u32 khz = head->asy.hz / 1000; + struct nvkm_outp *outp; + struct nvkm_ior *ior; + + /* Determine which OR, if any, we're attaching from the head. */ + HEAD_DBG(head, "supervisor 2.2"); + ior = nv50_disp_super_ior_asy(head); + if (!ior) return; - /* we allow both encoder attach and detach operations to occur - * within a single supervisor (ie. modeset) sequence. the - * encoder detach scripts quite often switch off power to the - * lanes, which requires the link to be re-trained. - * - * this is not generally an issue as the sink "must" (heh) - * signal an irq when it's lost sync so the driver can - * re-train. + /* For some reason, NVIDIA decided not to: * - * however, on some boards, if one does not configure at least - * the gpu side of the link *before* attaching, then various - * things can go horribly wrong (PDISP disappearing from mmio, - * third supervisor never happens, etc). + * A) Give dual-link LVDS a separate EVO protocol, like for TMDS. + * and + * B) Use SetControlOutputResource.PixelDepth on LVDS. * - * the solution is simply to retrain here, if necessary. last - * i checked, the binary driver userspace does not appear to - * trigger this situation (it forces an UPDATE between steps). + * Override the values we usually read from HW with the same + * data we pass though an ioctl instead. */ - if (outp->info.type == DCB_OUTPUT_DP) { - u32 soff = (ffs(outp->info.or) - 1) * 0x08; - u32 ctrl, datarate; - - if (outp->info.location == 0) { - ctrl = nvkm_rd32(device, 0x610794 + soff); - soff = 1; - } else { - ctrl = nvkm_rd32(device, 0x610b80 + soff); - soff = 2; - } + if (ior->type == SOR && ior->asy.proto == LVDS) { + head->asy.or.depth = (disp->sor.lvdsconf & 0x0200) ? 24 : 18; + ior->asy.link = (disp->sor.lvdsconf & 0x0100) ? 3 : 1; + } - switch ((ctrl & 0x000f0000) >> 16) { - case 6: datarate = pclk * 30; break; - case 5: datarate = pclk * 24; break; - case 2: - default: - datarate = pclk * 18; - break; - } + /* Handle any link training, etc. */ + if ((outp = ior->asy.outp) && outp->func->acquire) + outp->func->acquire(outp); - if (nvkm_output_dp_train(outp, datarate / soff)) - OUTP_ERR(outp, "link not trained before attach"); - } + /* Execute OnInt2 IED script. */ + nv50_disp_super_ied_on(head, ior, 0, khz); - exec_clkcmp(disp, head, 0, pclk, &conf); + /* Program RG clock divider. */ + head->func->rgclk(head, ior->asy.rgdiv); - if (!outp->info.location && outp->info.type == DCB_OUTPUT_ANALOG) { - oreg = 0x614280 + (ffs(outp->info.or) - 1) * 0x800; - oval = 0x00000000; - hval = 0x00000000; - mask = 0xffffffff; - } else - if (!outp->info.location) { - if (outp->info.type == DCB_OUTPUT_DP) - nv50_disp_intr_unk20_2_dp(disp, head, &outp->info, pclk); - oreg = 0x614300 + (ffs(outp->info.or) - 1) * 0x800; - oval = (conf & 0x0100) ? 0x00000101 : 0x00000000; - hval = 0x00000000; - mask = 0x00000707; - } else { - oreg = 0x614380 + (ffs(outp->info.or) - 1) * 0x800; - oval = 0x00000001; - hval = 0x00000001; - mask = 0x00000707; - } + /* Mode-specific internal DP configuration. */ + if (ior->type == SOR && ior->asy.proto == DP) + nv50_disp_super_2_2_dp(head, ior); - nvkm_mask(device, hreg, 0x0000000f, hval); - nvkm_mask(device, oreg, mask, oval); + /* OR-specific handling. */ + ior->func->clock(ior); + if (ior->func->war_2) + ior->func->war_2(ior); +} - nv50_disp_dptmds_war_2(disp, &outp->info); +void +nv50_disp_super_2_1(struct nv50_disp *disp, struct nvkm_head *head) +{ + struct nvkm_devinit *devinit = disp->base.engine.subdev.device->devinit; + const u32 khz = head->asy.hz / 1000; + HEAD_DBG(head, "supervisor 2.1 - %d khz", khz); + if (khz) + nvkm_devinit_pll_set(devinit, PLL_VPLL0 + head->id, khz); } -/* If programming a TMDS output on a SOR that can also be configured for - * DisplayPort, make sure NV50_SOR_DP_CTRL_ENABLE is forced off. - * - * It looks like the VBIOS TMDS scripts make an attempt at this, however, - * the VBIOS scripts on at least one board I have only switch it off on - * link 0, causing a blank display if the output has previously been - * programmed for DisplayPort. - */ -static void -nv50_disp_intr_unk40_0_tmds(struct nv50_disp *disp, - struct dcb_output *outp) +void +nv50_disp_super_2_0(struct nv50_disp *disp, struct nvkm_head *head) { - struct nvkm_device *device = disp->base.engine.subdev.device; - struct nvkm_bios *bios = device->bios; - const int link = !(outp->sorconf.link & 1); - const int or = ffs(outp->or) - 1; - const u32 loff = (or * 0x800) + (link * 0x80); - const u16 mask = (outp->sorconf.link << 6) | outp->or; - struct dcb_output match; - u8 ver, hdr; - - if (dcb_outp_match(bios, DCB_OUTPUT_DP, mask, &ver, &hdr, &match)) - nvkm_mask(device, 0x61c10c + loff, 0x00000001, 0x00000000); + struct nvkm_outp *outp; + struct nvkm_ior *ior; + + /* Determine which OR, if any, we're detaching from the head. */ + HEAD_DBG(head, "supervisor 2.0"); + ior = nv50_disp_super_ior_arm(head); + if (!ior) + return; + + /* Execute OffInt2 IED script. */ + nv50_disp_super_ied_off(head, ior, 2); + + /* If we're shutting down the OR's only active head, execute + * the output path's release function. + */ + if (ior->arm.head == (1 << head->id)) { + if ((outp = ior->arm.outp) && outp->func->release) + outp->func->release(outp, ior); + } } -static void -nv50_disp_intr_unk40_0(struct nv50_disp *disp, int head) +void +nv50_disp_super_1_0(struct nv50_disp *disp, struct nvkm_head *head) { - struct nvkm_device *device = disp->base.engine.subdev.device; - struct nvkm_output *outp; - u32 pclk = nvkm_rd32(device, 0x610ad0 + (head * 0x540)) & 0x3fffff; - u32 conf; + struct nvkm_ior *ior; - outp = exec_clkcmp(disp, head, 1, pclk, &conf); - if (!outp) + /* Determine which OR, if any, we're detaching from the head. */ + HEAD_DBG(head, "supervisor 1.0"); + ior = nv50_disp_super_ior_arm(head); + if (!ior) return; - if (outp->info.location == 0 && outp->info.type == DCB_OUTPUT_TMDS) - nv50_disp_intr_unk40_0_tmds(disp, &outp->info); - nv50_disp_dptmds_war_3(disp, &outp->info); + /* Execute OffInt1 IED script. */ + nv50_disp_super_ied_off(head, ior, 1); } void -nv50_disp_intr_supervisor(struct work_struct *work) +nv50_disp_super_1(struct nv50_disp *disp) +{ + struct nvkm_head *head; + struct nvkm_ior *ior; + + list_for_each_entry(head, &disp->base.head, head) { + head->func->state(head, &head->arm); + head->func->state(head, &head->asy); + } + + list_for_each_entry(ior, &disp->base.ior, head) { + ior->func->state(ior, &ior->arm); + ior->func->state(ior, &ior->asy); + } +} + +void +nv50_disp_super(struct work_struct *work) { struct nv50_disp *disp = container_of(work, struct nv50_disp, supervisor); struct nvkm_subdev *subdev = &disp->base.engine.subdev; struct nvkm_device *device = subdev->device; + struct nvkm_head *head; u32 super = nvkm_rd32(device, 0x610030); - int head; nvkm_debug(subdev, "supervisor %08x %08x\n", disp->super, super); if (disp->super & 0x00000010) { nv50_disp_chan_mthd(disp->chan[0], NV_DBG_DEBUG); - for (head = 0; head < disp->base.head.nr; head++) { - if (!(super & (0x00000020 << head))) + nv50_disp_super_1(disp); + list_for_each_entry(head, &disp->base.head, head) { + if (!(super & (0x00000020 << head->id))) continue; - if (!(super & (0x00000080 << head))) + if (!(super & (0x00000080 << head->id))) continue; - nv50_disp_intr_unk10_0(disp, head); + nv50_disp_super_1_0(disp, head); } } else if (disp->super & 0x00000020) { - for (head = 0; head < disp->base.head.nr; head++) { - if (!(super & (0x00000080 << head))) + list_for_each_entry(head, &disp->base.head, head) { + if (!(super & (0x00000080 << head->id))) continue; - nv50_disp_intr_unk20_0(disp, head); + nv50_disp_super_2_0(disp, head); } - for (head = 0; head < disp->base.head.nr; head++) { - if (!(super & (0x00000200 << head))) + nvkm_outp_route(&disp->base); + list_for_each_entry(head, &disp->base.head, head) { + if (!(super & (0x00000200 << head->id))) continue; - nv50_disp_intr_unk20_1(disp, head); + nv50_disp_super_2_1(disp, head); } - for (head = 0; head < disp->base.head.nr; head++) { - if (!(super & (0x00000080 << head))) + list_for_each_entry(head, &disp->base.head, head) { + if (!(super & (0x00000080 << head->id))) continue; - nv50_disp_intr_unk20_2(disp, head); + nv50_disp_super_2_2(disp, head); } } else if (disp->super & 0x00000040) { - for (head = 0; head < disp->base.head.nr; head++) { - if (!(super & (0x00000080 << head))) + list_for_each_entry(head, &disp->base.head, head) { + if (!(super & (0x00000080 << head->id))) continue; - nv50_disp_intr_unk40_0(disp, head); + nv50_disp_super_3_0(disp, head); } - nv50_disp_update_sppll1(disp); } nvkm_wr32(device, 0x610030, 0x80000000); } +static const struct nvkm_enum +nv50_disp_intr_error_type[] = { + { 3, "ILLEGAL_MTHD" }, + { 4, "INVALID_VALUE" }, + { 5, "INVALID_STATE" }, + { 7, "INVALID_HANDLE" }, + {} +}; + +static const struct nvkm_enum +nv50_disp_intr_error_code[] = { + { 0x00, "" }, + {} +}; + +static void +nv50_disp_intr_error(struct nv50_disp *disp, int chid) +{ + struct nvkm_subdev *subdev = &disp->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 data = nvkm_rd32(device, 0x610084 + (chid * 0x08)); + u32 addr = nvkm_rd32(device, 0x610080 + (chid * 0x08)); + u32 code = (addr & 0x00ff0000) >> 16; + u32 type = (addr & 0x00007000) >> 12; + u32 mthd = (addr & 0x00000ffc); + const struct nvkm_enum *ec, *et; + + et = nvkm_enum_find(nv50_disp_intr_error_type, type); + ec = nvkm_enum_find(nv50_disp_intr_error_code, code); + + nvkm_error(subdev, + "ERROR %d [%s] %02x [%s] chid %d mthd %04x data %08x\n", + type, et ? et->name : "", code, ec ? ec->name : "", + chid, mthd, data); + + if (chid < ARRAY_SIZE(disp->chan)) { + switch (mthd) { + case 0x0080: + nv50_disp_chan_mthd(disp->chan[chid], NV_DBG_ERROR); + break; + default: + break; + } + } + + nvkm_wr32(device, 0x610020, 0x00010000 << chid); + nvkm_wr32(device, 0x610080 + (chid * 0x08), 0x90000000); +} + void nv50_disp_intr(struct nv50_disp *disp) { @@ -934,7 +608,7 @@ nv50_disp_intr(struct nv50_disp *disp) if (intr1 & 0x00000070) { disp->super = (intr1 & 0x00000070); - schedule_work(&disp->supervisor); + queue_work(disp->wq, &disp->supervisor); nvkm_wr32(device, 0x610024, disp->super); } } @@ -943,23 +617,12 @@ static const struct nv50_disp_func nv50_disp = { .intr = nv50_disp_intr, .uevent = &nv50_disp_chan_uevent, - .super = nv50_disp_intr_supervisor, + .super = nv50_disp_super, .root = &nv50_disp_root_oclass, - .head.vblank_init = nv50_disp_vblank_init, - .head.vblank_fini = nv50_disp_vblank_fini, - .head.scanoutpos = nv50_disp_root_scanoutpos, - .outp.internal.crt = nv50_dac_output_new, - .outp.internal.tmds = nv50_sor_output_new, - .outp.internal.lvds = nv50_sor_output_new, - .outp.external.tmds = nv50_pior_output_new, - .outp.external.dp = nv50_pior_dp_new, - .dac.nr = 3, - .dac.power = nv50_dac_power, - .dac.sense = nv50_dac_sense, - .sor.nr = 2, - .sor.power = nv50_sor_power, - .pior.nr = 3, - .pior.power = nv50_pior_power, + .head.new = nv50_head_new, + .dac = { .nr = 3, .new = nv50_dac_new }, + .sor = { .nr = 2, .new = nv50_sor_new }, + .pior = { .nr = 3, .new = nv50_pior_new }, }; int diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h index 1e1de6bfe85a..19c635663399 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h @@ -2,18 +2,13 @@ #define __NV50_DISP_H__ #define nv50_disp(p) container_of((p), struct nv50_disp, base) #include "priv.h" -struct nvkm_output; -struct nvkm_output_dp; - -#define NV50_DISP_MTHD_ struct nvkm_object *object, \ - struct nv50_disp *disp, void *data, u32 size -#define NV50_DISP_MTHD_V0 NV50_DISP_MTHD_, int head -#define NV50_DISP_MTHD_V1 NV50_DISP_MTHD_, int head, struct nvkm_output *outp +struct nvkm_head; struct nv50_disp { const struct nv50_disp_func *func; struct nvkm_disp base; + struct workqueue_struct *wq; struct work_struct supervisor; u32 super; @@ -30,42 +25,18 @@ struct nv50_disp { struct nv50_disp_chan *chan[17]; }; -int nv50_disp_root_scanoutpos(NV50_DISP_MTHD_V0); - -int gf119_disp_root_scanoutpos(NV50_DISP_MTHD_V0); - -int nv50_dac_power(NV50_DISP_MTHD_V1); -int nv50_dac_sense(NV50_DISP_MTHD_V1); - -int gt215_hda_eld(NV50_DISP_MTHD_V1); -int gf119_hda_eld(NV50_DISP_MTHD_V1); - -int g84_hdmi_ctrl(NV50_DISP_MTHD_V1); -int gt215_hdmi_ctrl(NV50_DISP_MTHD_V1); -int gf119_hdmi_ctrl(NV50_DISP_MTHD_V1); -int gk104_hdmi_ctrl(NV50_DISP_MTHD_V1); - -int nv50_sor_power(NV50_DISP_MTHD_V1); -int nv50_pior_power(NV50_DISP_MTHD_V1); +void nv50_disp_super_1(struct nv50_disp *); +void nv50_disp_super_1_0(struct nv50_disp *, struct nvkm_head *); +void nv50_disp_super_2_0(struct nv50_disp *, struct nvkm_head *); +void nv50_disp_super_2_1(struct nv50_disp *, struct nvkm_head *); +void nv50_disp_super_2_2(struct nv50_disp *, struct nvkm_head *); +void nv50_disp_super_3_0(struct nv50_disp *, struct nvkm_head *); int nv50_disp_new_(const struct nv50_disp_func *, struct nvkm_device *, int index, int heads, struct nvkm_disp **); int gf119_disp_new_(const struct nv50_disp_func *, struct nvkm_device *, int index, struct nvkm_disp **); -struct nv50_disp_func_outp { - int (* crt)(struct nvkm_disp *, int index, struct dcb_output *, - struct nvkm_output **); - int (* tv)(struct nvkm_disp *, int index, struct dcb_output *, - struct nvkm_output **); - int (*tmds)(struct nvkm_disp *, int index, struct dcb_output *, - struct nvkm_output **); - int (*lvds)(struct nvkm_disp *, int index, struct dcb_output *, - struct nvkm_output **); - int (* dp)(struct nvkm_disp *, int index, struct dcb_output *, - struct nvkm_output **); -}; - struct nv50_disp_func { void (*intr)(struct nv50_disp *); void (*intr_error)(struct nv50_disp *, int chid); @@ -76,44 +47,33 @@ struct nv50_disp_func { const struct nvkm_disp_oclass *root; struct { - void (*vblank_init)(struct nv50_disp *, int head); - void (*vblank_fini)(struct nv50_disp *, int head); - int (*scanoutpos)(NV50_DISP_MTHD_V0); + int (*new)(struct nvkm_disp *, int id); } head; struct { - const struct nv50_disp_func_outp internal; - const struct nv50_disp_func_outp external; - } outp; - - struct { int nr; - int (*power)(NV50_DISP_MTHD_V1); - int (*sense)(NV50_DISP_MTHD_V1); + int (*new)(struct nvkm_disp *, int id); } dac; struct { int nr; - int (*power)(NV50_DISP_MTHD_V1); - int (*hda_eld)(NV50_DISP_MTHD_V1); - int (*hdmi)(NV50_DISP_MTHD_V1); - void (*magic)(struct nvkm_output *); + int (*new)(struct nvkm_disp *, int id); } sor; struct { int nr; - int (*power)(NV50_DISP_MTHD_V1); + int (*new)(struct nvkm_disp *, int id); } pior; }; -void nv50_disp_vblank_init(struct nv50_disp *, int); -void nv50_disp_vblank_fini(struct nv50_disp *, int); void nv50_disp_intr(struct nv50_disp *); -void nv50_disp_intr_supervisor(struct work_struct *); +void nv50_disp_super(struct work_struct *); -void gf119_disp_vblank_init(struct nv50_disp *, int); -void gf119_disp_vblank_fini(struct nv50_disp *, int); void gf119_disp_intr(struct nv50_disp *); -void gf119_disp_intr_supervisor(struct work_struct *); +void gf119_disp_super(struct work_struct *); void gf119_disp_intr_error(struct nv50_disp *, int); + +void nv50_disp_dptmds_war_2(struct nv50_disp *, struct dcb_output *); +void nv50_disp_dptmds_war_3(struct nv50_disp *, struct dcb_output *); +void nv50_disp_update_sppll1(struct nv50_disp *); #endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/oimmnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/oimmnv50.c index 07540f3d32dc..f3b0fa2c5924 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/oimmnv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/oimmnv50.c @@ -22,6 +22,7 @@ * Authors: Ben Skeggs */ #include "channv50.h" +#include "head.h" #include "rootnv50.h" #include <core/client.h> @@ -48,7 +49,7 @@ nv50_disp_oimm_new(const struct nv50_disp_chan_func *func, if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { nvif_ioctl(parent, "create disp overlay vers %d head %d\n", args->v0.version, args->v0.head); - if (args->v0.head > disp->base.head.nr) + if (!nvkm_head_find(&disp->base, args->v0.head)) return -EINVAL; head = args->v0.head; } else diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c index bbe5ec0dedb2..85aff85394ac 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c @@ -22,29 +22,207 @@ * Authors: Ben Skeggs */ #include "outp.h" +#include "ior.h" #include <subdev/bios.h> #include <subdev/bios/dcb.h> #include <subdev/i2c.h> void -nvkm_output_fini(struct nvkm_output *outp) +nvkm_outp_route(struct nvkm_disp *disp) +{ + struct nvkm_outp *outp; + struct nvkm_ior *ior; + + list_for_each_entry(ior, &disp->ior, head) { + if ((outp = ior->arm.outp) && ior->arm.outp != ior->asy.outp) { + OUTP_DBG(outp, "release %s", ior->name); + if (ior->func->route.set) + ior->func->route.set(outp, NULL); + ior->arm.outp = NULL; + } + } + + list_for_each_entry(ior, &disp->ior, head) { + if ((outp = ior->asy.outp)) { + OUTP_DBG(outp, "acquire %s", ior->name); + if (ior->asy.outp != ior->arm.outp) { + if (ior->func->route.set) + ior->func->route.set(outp, ior); + ior->arm.outp = ior->asy.outp; + } + } + } +} + +static enum nvkm_ior_proto +nvkm_outp_xlat(struct nvkm_outp *outp, enum nvkm_ior_type *type) +{ + switch (outp->info.location) { + case 0: + switch (outp->info.type) { + case DCB_OUTPUT_ANALOG: *type = DAC; return CRT; + case DCB_OUTPUT_TMDS : *type = SOR; return TMDS; + case DCB_OUTPUT_LVDS : *type = SOR; return LVDS; + case DCB_OUTPUT_DP : *type = SOR; return DP; + default: + break; + } + break; + case 1: + switch (outp->info.type) { + case DCB_OUTPUT_TMDS: *type = PIOR; return TMDS; + case DCB_OUTPUT_DP : *type = PIOR; return TMDS; /* not a bug */ + default: + break; + } + break; + default: + break; + } + WARN_ON(1); + return UNKNOWN; +} + +void +nvkm_outp_release(struct nvkm_outp *outp, u8 user) +{ + struct nvkm_ior *ior = outp->ior; + OUTP_TRACE(outp, "release %02x &= %02x %p", outp->acquired, ~user, ior); + if (ior) { + outp->acquired &= ~user; + if (!outp->acquired) { + outp->ior->asy.outp = NULL; + outp->ior = NULL; + } + } +} + +static inline int +nvkm_outp_acquire_ior(struct nvkm_outp *outp, u8 user, struct nvkm_ior *ior) +{ + outp->ior = ior; + outp->ior->asy.outp = outp; + outp->ior->asy.link = outp->info.sorconf.link; + outp->acquired |= user; + return 0; +} + +int +nvkm_outp_acquire(struct nvkm_outp *outp, u8 user) +{ + struct nvkm_ior *ior = outp->ior; + enum nvkm_ior_proto proto; + enum nvkm_ior_type type; + + OUTP_TRACE(outp, "acquire %02x |= %02x %p", outp->acquired, user, ior); + if (ior) { + outp->acquired |= user; + return 0; + } + + /* Lookup a compatible, and unused, OR to assign to the device. */ + proto = nvkm_outp_xlat(outp, &type); + if (proto == UNKNOWN) + return -ENOSYS; + + /* First preference is to reuse the OR that is currently armed + * on HW, if any, in order to prevent unnecessary switching. + */ + list_for_each_entry(ior, &outp->disp->ior, head) { + if (!ior->asy.outp && ior->arm.outp == outp) + return nvkm_outp_acquire_ior(outp, user, ior); + } + + /* Failing that, a completely unused OR is the next best thing. */ + list_for_each_entry(ior, &outp->disp->ior, head) { + if (!ior->asy.outp && ior->type == type && !ior->arm.outp && + (ior->func->route.set || ior->id == __ffs(outp->info.or))) + return nvkm_outp_acquire_ior(outp, user, ior); + } + + /* Last resort is to assign an OR that's already active on HW, + * but will be released during the next modeset. + */ + list_for_each_entry(ior, &outp->disp->ior, head) { + if (!ior->asy.outp && ior->type == type && + (ior->func->route.set || ior->id == __ffs(outp->info.or))) + return nvkm_outp_acquire_ior(outp, user, ior); + } + + return -ENOSPC; +} + +void +nvkm_outp_fini(struct nvkm_outp *outp) { if (outp->func->fini) outp->func->fini(outp); } +static void +nvkm_outp_init_route(struct nvkm_outp *outp) +{ + struct nvkm_disp *disp = outp->disp; + enum nvkm_ior_proto proto; + enum nvkm_ior_type type; + struct nvkm_ior *ior; + int id, link; + + /* Find any OR from the class that is able to support this device. */ + proto = nvkm_outp_xlat(outp, &type); + if (proto == UNKNOWN) + return; + + ior = nvkm_ior_find(disp, type, -1); + if (!ior) { + WARN_ON(1); + return; + } + + /* Determine the specific OR, if any, this device is attached to. */ + if (ior->func->route.get) { + id = ior->func->route.get(outp, &link); + if (id < 0) { + OUTP_DBG(outp, "no route"); + return; + } + } else { + /* Prior to DCB 4.1, this is hardwired like so. */ + id = ffs(outp->info.or) - 1; + link = (ior->type == SOR) ? outp->info.sorconf.link : 0; + } + + ior = nvkm_ior_find(disp, type, id); + if (!ior) { + WARN_ON(1); + return; + } + + /* Determine if the OR is already configured for this device. */ + ior->func->state(ior, &ior->arm); + if (!ior->arm.head || ior->arm.proto != proto) { + OUTP_DBG(outp, "no heads (%x %d %d)", ior->arm.head, + ior->arm.proto, proto); + return; + } + + OUTP_DBG(outp, "on %s link %x", ior->name, ior->arm.link); + ior->arm.outp = outp; +} + void -nvkm_output_init(struct nvkm_output *outp) +nvkm_outp_init(struct nvkm_outp *outp) { + nvkm_outp_init_route(outp); if (outp->func->init) outp->func->init(outp); } void -nvkm_output_del(struct nvkm_output **poutp) +nvkm_outp_del(struct nvkm_outp **poutp) { - struct nvkm_output *outp = *poutp; + struct nvkm_outp *outp = *poutp; if (outp && !WARN_ON(!outp->func)) { if (outp->func->dtor) *poutp = outp->func->dtor(outp); @@ -53,11 +231,13 @@ nvkm_output_del(struct nvkm_output **poutp) } } -void -nvkm_output_ctor(const struct nvkm_output_func *func, struct nvkm_disp *disp, - int index, struct dcb_output *dcbE, struct nvkm_output *outp) +int +nvkm_outp_ctor(const struct nvkm_outp_func *func, struct nvkm_disp *disp, + int index, struct dcb_output *dcbE, struct nvkm_outp *outp) { struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c; + enum nvkm_ior_proto proto; + enum nvkm_ior_type type; outp->func = func; outp->disp = disp; @@ -72,16 +252,24 @@ nvkm_output_ctor(const struct nvkm_output_func *func, struct nvkm_disp *disp, outp->info.type >= 2 ? outp->info.sorconf.link : 0, outp->info.connector, outp->info.i2c_index, outp->info.bus, outp->info.heads); + + /* Cull output paths we can't map to an output resource. */ + proto = nvkm_outp_xlat(outp, &type); + if (proto == UNKNOWN) + return -ENODEV; + + return 0; } +static const struct nvkm_outp_func +nvkm_outp = { +}; + int -nvkm_output_new_(const struct nvkm_output_func *func, - struct nvkm_disp *disp, int index, struct dcb_output *dcbE, - struct nvkm_output **poutp) +nvkm_outp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, + struct nvkm_outp **poutp) { if (!(*poutp = kzalloc(sizeof(**poutp), GFP_KERNEL))) return -ENOMEM; - - nvkm_output_ctor(func, disp, index, dcbE, *poutp); - return 0; + return nvkm_outp_ctor(&nvkm_outp, disp, index, dcbE, *poutp); } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h index 07727198d7ce..146d101d4891 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h @@ -5,47 +5,46 @@ #include <subdev/bios.h> #include <subdev/bios/dcb.h> -struct nvkm_output { - const struct nvkm_output_func *func; +struct nvkm_outp { + const struct nvkm_outp_func *func; struct nvkm_disp *disp; int index; struct dcb_output info; - // whatever (if anything) is pointed at by the dcb device entry struct nvkm_i2c_bus *i2c; int or; struct list_head head; - struct nvkm_connector *conn; -}; + struct nvkm_conn *conn; -struct nvkm_output_func { - void *(*dtor)(struct nvkm_output *); - void (*init)(struct nvkm_output *); - void (*fini)(struct nvkm_output *); + /* Assembly state. */ +#define NVKM_OUTP_PRIV 1 +#define NVKM_OUTP_USER 2 + u8 acquired:2; + struct nvkm_ior *ior; }; -void nvkm_output_ctor(const struct nvkm_output_func *, struct nvkm_disp *, - int index, struct dcb_output *, struct nvkm_output *); -int nvkm_output_new_(const struct nvkm_output_func *, struct nvkm_disp *, - int index, struct dcb_output *, struct nvkm_output **); -void nvkm_output_del(struct nvkm_output **); -void nvkm_output_init(struct nvkm_output *); -void nvkm_output_fini(struct nvkm_output *); - -int nv50_dac_output_new(struct nvkm_disp *, int, struct dcb_output *, - struct nvkm_output **); -int nv50_sor_output_new(struct nvkm_disp *, int, struct dcb_output *, - struct nvkm_output **); -int nv50_pior_output_new(struct nvkm_disp *, int, struct dcb_output *, - struct nvkm_output **); - -u32 g94_sor_dp_lane_map(struct nvkm_device *, u8 lane); - -void gm200_sor_magic(struct nvkm_output *outp); +int nvkm_outp_ctor(const struct nvkm_outp_func *, struct nvkm_disp *, + int index, struct dcb_output *, struct nvkm_outp *); +int nvkm_outp_new(struct nvkm_disp *, int index, struct dcb_output *, + struct nvkm_outp **); +void nvkm_outp_del(struct nvkm_outp **); +void nvkm_outp_init(struct nvkm_outp *); +void nvkm_outp_fini(struct nvkm_outp *); +int nvkm_outp_acquire(struct nvkm_outp *, u8 user); +void nvkm_outp_release(struct nvkm_outp *, u8 user); +void nvkm_outp_route(struct nvkm_disp *); + +struct nvkm_outp_func { + void *(*dtor)(struct nvkm_outp *); + void (*init)(struct nvkm_outp *); + void (*fini)(struct nvkm_outp *); + int (*acquire)(struct nvkm_outp *); + void (*release)(struct nvkm_outp *, struct nvkm_ior *); +}; #define OUTP_MSG(o,l,f,a...) do { \ - struct nvkm_output *_outp = (o); \ + struct nvkm_outp *_outp = (o); \ nvkm_##l(&_outp->disp->engine.subdev, "outp %02x:%04x:%04x: "f"\n", \ _outp->index, _outp->info.hasht, _outp->info.hashm, ##a); \ } while(0) diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.c deleted file mode 100644 index de36f73b14dc..000000000000 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.c +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright 2014 Red Hat Inc. - * - * 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 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 HOLDER(S) OR AUTHOR(S) 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. - * - * Authors: Ben Skeggs - */ -#include "outpdp.h" -#include "conn.h" -#include "dport.h" -#include "priv.h" - -#include <subdev/i2c.h> - -#include <nvif/event.h> - -int -nvkm_output_dp_train(struct nvkm_output *base, u32 datarate) -{ - struct nvkm_output_dp *outp = nvkm_output_dp(base); - bool retrain = true; - u8 link[2], stat[3]; - u32 linkrate; - int ret, i; - - mutex_lock(&outp->mutex); - - /* check that the link is trained at a high enough rate */ - ret = nvkm_rdaux(outp->aux, DPCD_LC00_LINK_BW_SET, link, 2); - if (ret) { - OUTP_DBG(&outp->base, - "failed to read link config, assuming no sink"); - goto done; - } - - linkrate = link[0] * 27000 * (link[1] & DPCD_LC01_LANE_COUNT_SET); - linkrate = (linkrate * 8) / 10; /* 8B/10B coding overhead */ - datarate = (datarate + 9) / 10; /* -> decakilobits */ - if (linkrate < datarate) { - OUTP_DBG(&outp->base, "link not trained at sufficient rate"); - goto done; - } - - /* check that link is still trained */ - ret = nvkm_rdaux(outp->aux, DPCD_LS02, stat, 3); - if (ret) { - OUTP_DBG(&outp->base, - "failed to read link status, assuming no sink"); - goto done; - } - - if (stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE) { - for (i = 0; i < (link[1] & DPCD_LC01_LANE_COUNT_SET); i++) { - u8 lane = (stat[i >> 1] >> ((i & 1) * 4)) & 0x0f; - if (!(lane & DPCD_LS02_LANE0_CR_DONE) || - !(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) || - !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) { - OUTP_DBG(&outp->base, - "lane %d not equalised", lane); - goto done; - } - } - retrain = false; - } else { - OUTP_DBG(&outp->base, "no inter-lane alignment"); - } - -done: - if (retrain || !atomic_read(&outp->lt.done)) { - /* no sink, but still need to configure source */ - if (outp->dpcd[DPCD_RC00_DPCD_REV] == 0x00) { - outp->dpcd[DPCD_RC01_MAX_LINK_RATE] = - outp->base.info.dpconf.link_bw; - outp->dpcd[DPCD_RC02] = - outp->base.info.dpconf.link_nr; - } - nvkm_dp_train(outp); - } - - mutex_unlock(&outp->mutex); - return ret; -} - -static void -nvkm_output_dp_enable(struct nvkm_output_dp *outp, bool enable) -{ - struct nvkm_i2c_aux *aux = outp->aux; - - if (enable) { - if (!outp->present) { - OUTP_DBG(&outp->base, "aux power -> always"); - nvkm_i2c_aux_monitor(aux, true); - outp->present = true; - } - - if (!nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, outp->dpcd, - sizeof(outp->dpcd))) { - nvkm_output_dp_train(&outp->base, 0); - return; - } - } - - if (outp->present) { - OUTP_DBG(&outp->base, "aux power -> demand"); - nvkm_i2c_aux_monitor(aux, false); - outp->present = false; - } - - atomic_set(&outp->lt.done, 0); -} - -static int -nvkm_output_dp_hpd(struct nvkm_notify *notify) -{ - const struct nvkm_i2c_ntfy_rep *line = notify->data; - struct nvkm_output_dp *outp = container_of(notify, typeof(*outp), hpd); - struct nvkm_connector *conn = outp->base.conn; - struct nvkm_disp *disp = outp->base.disp; - struct nvif_notify_conn_rep_v0 rep = {}; - - OUTP_DBG(&outp->base, "HPD: %d", line->mask); - nvkm_output_dp_enable(outp, true); - - if (line->mask & NVKM_I2C_UNPLUG) - rep.mask |= NVIF_NOTIFY_CONN_V0_UNPLUG; - if (line->mask & NVKM_I2C_PLUG) - rep.mask |= NVIF_NOTIFY_CONN_V0_PLUG; - - nvkm_event_send(&disp->hpd, rep.mask, conn->index, &rep, sizeof(rep)); - return NVKM_NOTIFY_KEEP; -} - -static int -nvkm_output_dp_irq(struct nvkm_notify *notify) -{ - const struct nvkm_i2c_ntfy_rep *line = notify->data; - struct nvkm_output_dp *outp = container_of(notify, typeof(*outp), irq); - struct nvkm_connector *conn = outp->base.conn; - struct nvkm_disp *disp = outp->base.disp; - struct nvif_notify_conn_rep_v0 rep = { - .mask = NVIF_NOTIFY_CONN_V0_IRQ, - }; - - OUTP_DBG(&outp->base, "IRQ: %d", line->mask); - nvkm_output_dp_train(&outp->base, 0); - - nvkm_event_send(&disp->hpd, rep.mask, conn->index, &rep, sizeof(rep)); - return NVKM_NOTIFY_KEEP; -} - -static void -nvkm_output_dp_fini(struct nvkm_output *base) -{ - struct nvkm_output_dp *outp = nvkm_output_dp(base); - nvkm_notify_put(&outp->hpd); - nvkm_notify_put(&outp->irq); - nvkm_output_dp_enable(outp, false); -} - -static void -nvkm_output_dp_init(struct nvkm_output *base) -{ - struct nvkm_output_dp *outp = nvkm_output_dp(base); - nvkm_notify_put(&outp->base.conn->hpd); - nvkm_output_dp_enable(outp, true); - nvkm_notify_get(&outp->irq); - nvkm_notify_get(&outp->hpd); -} - -static void * -nvkm_output_dp_dtor(struct nvkm_output *base) -{ - struct nvkm_output_dp *outp = nvkm_output_dp(base); - nvkm_notify_fini(&outp->hpd); - nvkm_notify_fini(&outp->irq); - return outp; -} - -static const struct nvkm_output_func -nvkm_output_dp_func = { - .dtor = nvkm_output_dp_dtor, - .init = nvkm_output_dp_init, - .fini = nvkm_output_dp_fini, -}; - -int -nvkm_output_dp_ctor(const struct nvkm_output_dp_func *func, - struct nvkm_disp *disp, int index, struct dcb_output *dcbE, - struct nvkm_i2c_aux *aux, struct nvkm_output_dp *outp) -{ - struct nvkm_device *device = disp->engine.subdev.device; - struct nvkm_bios *bios = device->bios; - struct nvkm_i2c *i2c = device->i2c; - u8 hdr, cnt, len; - u32 data; - int ret; - - nvkm_output_ctor(&nvkm_output_dp_func, disp, index, dcbE, &outp->base); - outp->func = func; - outp->aux = aux; - if (!outp->aux) { - OUTP_ERR(&outp->base, "no aux"); - return -ENODEV; - } - - /* bios data is not optional */ - data = nvbios_dpout_match(bios, outp->base.info.hasht, - outp->base.info.hashm, &outp->version, - &hdr, &cnt, &len, &outp->info); - if (!data) { - OUTP_ERR(&outp->base, "no bios dp data"); - return -ENODEV; - } - - OUTP_DBG(&outp->base, "bios dp %02x %02x %02x %02x", - outp->version, hdr, cnt, len); - - /* link maintenance */ - ret = nvkm_notify_init(NULL, &i2c->event, nvkm_output_dp_irq, true, - &(struct nvkm_i2c_ntfy_req) { - .mask = NVKM_I2C_IRQ, - .port = outp->aux->id, - }, - sizeof(struct nvkm_i2c_ntfy_req), - sizeof(struct nvkm_i2c_ntfy_rep), - &outp->irq); - if (ret) { - OUTP_ERR(&outp->base, "error monitoring aux irq: %d", ret); - return ret; - } - - mutex_init(&outp->mutex); - atomic_set(&outp->lt.done, 0); - - /* hotplug detect, replaces gpio-based mechanism with aux events */ - ret = nvkm_notify_init(NULL, &i2c->event, nvkm_output_dp_hpd, true, - &(struct nvkm_i2c_ntfy_req) { - .mask = NVKM_I2C_PLUG | NVKM_I2C_UNPLUG, - .port = outp->aux->id, - }, - sizeof(struct nvkm_i2c_ntfy_req), - sizeof(struct nvkm_i2c_ntfy_rep), - &outp->hpd); - if (ret) { - OUTP_ERR(&outp->base, "error monitoring aux hpd: %d", ret); - return ret; - } - - return 0; -} - -int -nvkm_output_dp_new_(const struct nvkm_output_dp_func *func, - struct nvkm_disp *disp, int index, struct dcb_output *dcbE, - struct nvkm_output **poutp) -{ - struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c; - struct nvkm_i2c_aux *aux = nvkm_i2c_aux_find(i2c, dcbE->i2c_index); - struct nvkm_output_dp *outp; - - if (!(outp = kzalloc(sizeof(*outp), GFP_KERNEL))) - return -ENOMEM; - *poutp = &outp->base; - - return nvkm_output_dp_ctor(func, disp, index, dcbE, aux, outp); -} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.h deleted file mode 100644 index 3c83a561cd88..000000000000 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.h +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef __NVKM_DISP_OUTP_DP_H__ -#define __NVKM_DISP_OUTP_DP_H__ -#define nvkm_output_dp(p) container_of((p), struct nvkm_output_dp, base) -#ifndef MSG -#define MSG(l,f,a...) \ - nvkm_##l(&outp->base.disp->engine.subdev, "%02x:%04x:%04x: "f, \ - outp->base.index, outp->base.info.hasht, \ - outp->base.info.hashm, ##a) -#define DBG(f,a...) MSG(debug, f, ##a) -#define ERR(f,a...) MSG(error, f, ##a) -#endif -#include "outp.h" - -#include <core/notify.h> -#include <subdev/bios.h> -#include <subdev/bios/dp.h> - -struct nvkm_output_dp { - const struct nvkm_output_dp_func *func; - struct nvkm_output base; - - struct nvbios_dpout info; - u8 version; - - struct nvkm_i2c_aux *aux; - - struct nvkm_notify irq; - struct nvkm_notify hpd; - bool present; - u8 dpcd[16]; - - struct mutex mutex; - struct { - atomic_t done; - bool mst; - } lt; -}; - -struct nvkm_output_dp_func { - int (*pattern)(struct nvkm_output_dp *, int); - int (*lnk_pwr)(struct nvkm_output_dp *, int nr); - int (*lnk_ctl)(struct nvkm_output_dp *, int nr, int bw, bool ef); - int (*drv_ctl)(struct nvkm_output_dp *, int ln, int vs, int pe, int pc); - void (*vcpi)(struct nvkm_output_dp *, int head, u8 start_slot, - u8 num_slots, u16 pbn, u16 aligned_pbn); -}; - -int nvkm_output_dp_train(struct nvkm_output *, u32 rate); - -int nvkm_output_dp_ctor(const struct nvkm_output_dp_func *, struct nvkm_disp *, - int index, struct dcb_output *, struct nvkm_i2c_aux *, - struct nvkm_output_dp *); -int nvkm_output_dp_new_(const struct nvkm_output_dp_func *, struct nvkm_disp *, - int index, struct dcb_output *, - struct nvkm_output **); - -int nv50_pior_dp_new(struct nvkm_disp *, int, struct dcb_output *, - struct nvkm_output **); - -int g94_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *, - struct nvkm_output **); -int g94_sor_dp_lnk_pwr(struct nvkm_output_dp *, int); - -int gf119_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *, - struct nvkm_output **); -int gf119_sor_dp_lnk_ctl(struct nvkm_output_dp *, int, int, bool); -int gf119_sor_dp_drv_ctl(struct nvkm_output_dp *, int, int, int, int); -void gf119_sor_dp_vcpi(struct nvkm_output_dp *, int, u8, u8, u16, u16); - -int gm107_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *, - struct nvkm_output **); -int gm107_sor_dp_pattern(struct nvkm_output_dp *, int); - -int gm200_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *, - struct nvkm_output **); -#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ovlynv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ovlynv50.c index 2a49c46425cd..9ebaaa6e9e33 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ovlynv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ovlynv50.c @@ -22,6 +22,7 @@ * Authors: Ben Skeggs */ #include "dmacnv50.h" +#include "head.h" #include "rootnv50.h" #include <core/client.h> @@ -50,7 +51,7 @@ nv50_disp_ovly_new(const struct nv50_disp_dmac_func *func, nvif_ioctl(parent, "create disp overlay channel dma vers %d " "pushbuf %016llx head %d\n", args->v0.version, args->v0.pushbuf, args->v0.head); - if (args->v0.head > disp->base.head.nr) + if (!nvkm_head_find(&disp->base, args->v0.head)) return -EINVAL; push = args->v0.pushbuf; head = args->v0.head; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/piornv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/piornv50.c index 6c532eadba17..99b3b9050635 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/piornv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/piornv50.c @@ -21,111 +21,114 @@ * * Authors: Ben Skeggs */ -#include "outpdp.h" -#include "nv50.h" +#include "ior.h" +#include "head.h" -#include <core/client.h> #include <subdev/i2c.h> #include <subdev/timer.h> -#include <nvif/cl5070.h> -#include <nvif/unpack.h> - -int -nv50_pior_power(NV50_DISP_MTHD_V1) +static void +nv50_pior_clock(struct nvkm_ior *pior) { - struct nvkm_device *device = disp->base.engine.subdev.device; - const u32 soff = outp->or * 0x800; - union { - struct nv50_disp_pior_pwr_v0 v0; - } *args = data; - u32 ctrl, type; - int ret = -ENOSYS; + struct nvkm_device *device = pior->disp->engine.subdev.device; + const u32 poff = nv50_ior_base(pior); + nvkm_mask(device, 0x614380 + poff, 0x00000707, 0x00000001); +} - nvif_ioctl(object, "disp pior pwr size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { - nvif_ioctl(object, "disp pior pwr vers %d state %d type %x\n", - args->v0.version, args->v0.state, args->v0.type); - if (args->v0.type > 0x0f) - return -EINVAL; - ctrl = !!args->v0.state; - type = args->v0.type; - } else +static int +nv50_pior_dp_links(struct nvkm_ior *pior, struct nvkm_i2c_aux *aux) +{ + int ret = nvkm_i2c_aux_lnk_ctl(aux, pior->dp.nr, pior->dp.bw, + pior->dp.ef); + if (ret) return ret; + return 1; +} +static void +nv50_pior_power_wait(struct nvkm_device *device, u32 poff) +{ nvkm_msec(device, 2000, - if (!(nvkm_rd32(device, 0x61e004 + soff) & 0x80000000)) - break; - ); - nvkm_mask(device, 0x61e004 + soff, 0x80000101, 0x80000000 | ctrl); - nvkm_msec(device, 2000, - if (!(nvkm_rd32(device, 0x61e004 + soff) & 0x80000000)) + if (!(nvkm_rd32(device, 0x61e004 + poff) & 0x80000000)) break; ); - disp->pior.type[outp->or] = type; - return 0; } -/****************************************************************************** - * TMDS - *****************************************************************************/ -static const struct nvkm_output_func -nv50_pior_output_func = { -}; - -int -nv50_pior_output_new(struct nvkm_disp *disp, int index, - struct dcb_output *dcbE, struct nvkm_output **poutp) +static void +nv50_pior_power(struct nvkm_ior *pior, bool normal, bool pu, + bool data, bool vsync, bool hsync) { - return nvkm_output_new_(&nv50_pior_output_func, disp, - index, dcbE, poutp); -} + struct nvkm_device *device = pior->disp->engine.subdev.device; + const u32 poff = nv50_ior_base(pior); + const u32 shift = normal ? 0 : 16; + const u32 state = 0x80000000 | (0x00000001 * !!pu) << shift; + const u32 field = 0x80000000 | (0x00000101 << shift); -/****************************************************************************** - * DisplayPort - *****************************************************************************/ -static int -nv50_pior_output_dp_pattern(struct nvkm_output_dp *outp, int pattern) -{ - return 0; + nv50_pior_power_wait(device, poff); + nvkm_mask(device, 0x61e004 + poff, field, state); + nv50_pior_power_wait(device, poff); } -static int -nv50_pior_output_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr) +void +nv50_pior_depth(struct nvkm_ior *ior, struct nvkm_ior_state *state, u32 ctrl) { - return 0; + /* GF119 moves this information to per-head methods, which is + * a lot more convenient, and where our shared code expect it. + */ + if (state->head && state == &ior->asy) { + struct nvkm_head *head = + nvkm_head_find(ior->disp, __ffs(state->head)); + if (!WARN_ON(!head)) { + struct nvkm_head_state *state = &head->asy; + switch ((ctrl & 0x000f0000) >> 16) { + case 6: state->or.depth = 30; break; + case 5: state->or.depth = 24; break; + case 2: state->or.depth = 18; break; + case 0: state->or.depth = 18; break; /*XXX*/ + default: + state->or.depth = 18; + WARN_ON(1); + break; + } + } + } } -static int -nv50_pior_output_dp_lnk_ctl(struct nvkm_output_dp *outp, - int nr, int bw, bool ef) +static void +nv50_pior_state(struct nvkm_ior *pior, struct nvkm_ior_state *state) { - int ret = nvkm_i2c_aux_lnk_ctl(outp->aux, nr, bw, ef); - if (ret) - return ret; - return 1; + struct nvkm_device *device = pior->disp->engine.subdev.device; + const u32 coff = pior->id * 8 + (state == &pior->arm) * 4; + u32 ctrl = nvkm_rd32(device, 0x610b80 + coff); + + state->proto_evo = (ctrl & 0x00000f00) >> 8; + state->rgdiv = 1; + switch (state->proto_evo) { + case 0: state->proto = TMDS; break; + default: + state->proto = UNKNOWN; + break; + } + + state->head = ctrl & 0x00000003; + nv50_pior_depth(pior, state, ctrl); } -static const struct nvkm_output_dp_func -nv50_pior_output_dp_func = { - .pattern = nv50_pior_output_dp_pattern, - .lnk_pwr = nv50_pior_output_dp_lnk_pwr, - .lnk_ctl = nv50_pior_output_dp_lnk_ctl, +static const struct nvkm_ior_func +nv50_pior = { + .state = nv50_pior_state, + .power = nv50_pior_power, + .clock = nv50_pior_clock, + .dp = { + .links = nv50_pior_dp_links, + }, }; int -nv50_pior_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, - struct nvkm_output **poutp) +nv50_pior_new(struct nvkm_disp *disp, int id) { - struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c; - struct nvkm_i2c_aux *aux = - nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_EXT(dcbE->extdev)); - struct nvkm_output_dp *outp; - - if (!(outp = kzalloc(sizeof(*outp), GFP_KERNEL))) - return -ENOMEM; - *poutp = &outp->base; - - return nvkm_output_dp_ctor(&nv50_pior_output_dp_func, disp, - index, dcbE, aux, outp); + struct nvkm_device *device = disp->engine.subdev.device; + if (!(nvkm_rd32(device, 0x610184) & (0x10000000 << id))) + return 0; + return nvkm_ior_new_(&nv50_pior, disp, PIOR, id); } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h index c2452957fc57..5772f0094129 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h @@ -2,42 +2,18 @@ #define __NVKM_DISP_PRIV_H__ #include <engine/disp.h> #include "outp.h" -#include "outpdp.h" int nvkm_disp_ctor(const struct nvkm_disp_func *, struct nvkm_device *, - int index, int heads, struct nvkm_disp *); + int index, struct nvkm_disp *); int nvkm_disp_new_(const struct nvkm_disp_func *, struct nvkm_device *, - int index, int heads, struct nvkm_disp **); + int index, struct nvkm_disp **); void nvkm_disp_vblank(struct nvkm_disp *, int head); -struct nvkm_disp_func_outp { - int (* crt)(struct nvkm_disp *, int index, struct dcb_output *, - struct nvkm_output **); - int (* tv)(struct nvkm_disp *, int index, struct dcb_output *, - struct nvkm_output **); - int (*tmds)(struct nvkm_disp *, int index, struct dcb_output *, - struct nvkm_output **); - int (*lvds)(struct nvkm_disp *, int index, struct dcb_output *, - struct nvkm_output **); - int (* dp)(struct nvkm_disp *, int index, struct dcb_output *, - struct nvkm_output **); -}; - struct nvkm_disp_func { void *(*dtor)(struct nvkm_disp *); void (*intr)(struct nvkm_disp *); const struct nvkm_disp_oclass *(*root)(struct nvkm_disp *); - - struct { - void (*vblank_init)(struct nvkm_disp *, int head); - void (*vblank_fini)(struct nvkm_disp *, int head); - } head; - - struct { - const struct nvkm_disp_func_outp internal; - const struct nvkm_disp_func_outp external; - } outp; }; int nvkm_disp_ntfy(struct nvkm_object *, u32, struct nvkm_event **); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootgf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootgf119.c index 335d88823c22..333c8424b413 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootgf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootgf119.c @@ -22,49 +22,13 @@ * Authors: Ben Skeggs */ #include "rootnv50.h" +#include "head.h" #include "dmacnv50.h" -#include <core/client.h> #include <core/ramht.h> #include <subdev/timer.h> #include <nvif/class.h> -#include <nvif/cl5070.h> -#include <nvif/unpack.h> - -int -gf119_disp_root_scanoutpos(NV50_DISP_MTHD_V0) -{ - struct nvkm_device *device = disp->base.engine.subdev.device; - const u32 total = nvkm_rd32(device, 0x640414 + (head * 0x300)); - const u32 blanke = nvkm_rd32(device, 0x64041c + (head * 0x300)); - const u32 blanks = nvkm_rd32(device, 0x640420 + (head * 0x300)); - union { - struct nv50_disp_scanoutpos_v0 v0; - } *args = data; - int ret = -ENOSYS; - - nvif_ioctl(object, "disp scanoutpos size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { - nvif_ioctl(object, "disp scanoutpos vers %d\n", - args->v0.version); - args->v0.vblanke = (blanke & 0xffff0000) >> 16; - args->v0.hblanke = (blanke & 0x0000ffff); - args->v0.vblanks = (blanks & 0xffff0000) >> 16; - args->v0.hblanks = (blanks & 0x0000ffff); - args->v0.vtotal = ( total & 0xffff0000) >> 16; - args->v0.htotal = ( total & 0x0000ffff); - args->v0.time[0] = ktime_to_ns(ktime_get()); - args->v0.vline = /* vline read locks hline */ - nvkm_rd32(device, 0x616340 + (head * 0x800)) & 0xffff; - args->v0.time[1] = ktime_to_ns(ktime_get()); - args->v0.hline = - nvkm_rd32(device, 0x616344 + (head * 0x800)) & 0xffff; - } else - return ret; - - return 0; -} void gf119_disp_root_fini(struct nv50_disp_root *root) @@ -78,6 +42,7 @@ int gf119_disp_root_init(struct nv50_disp_root *root) { struct nv50_disp *disp = root->disp; + struct nvkm_head *head; struct nvkm_device *device = disp->base.engine.subdev.device; u32 tmp; int i; @@ -88,13 +53,14 @@ gf119_disp_root_init(struct nv50_disp_root *root) */ /* ... CRTC caps */ - for (i = 0; i < disp->base.head.nr; i++) { - tmp = nvkm_rd32(device, 0x616104 + (i * 0x800)); - nvkm_wr32(device, 0x6101b4 + (i * 0x800), tmp); - tmp = nvkm_rd32(device, 0x616108 + (i * 0x800)); - nvkm_wr32(device, 0x6101b8 + (i * 0x800), tmp); - tmp = nvkm_rd32(device, 0x61610c + (i * 0x800)); - nvkm_wr32(device, 0x6101bc + (i * 0x800), tmp); + list_for_each_entry(head, &disp->base.head, head) { + const u32 hoff = head->id * 0x800; + tmp = nvkm_rd32(device, 0x616104 + hoff); + nvkm_wr32(device, 0x6101b4 + hoff, tmp); + tmp = nvkm_rd32(device, 0x616108 + hoff); + nvkm_wr32(device, 0x6101b8 + hoff, tmp); + tmp = nvkm_rd32(device, 0x61610c + hoff); + nvkm_wr32(device, 0x6101bc + hoff, tmp); } /* ... DAC caps */ @@ -134,8 +100,10 @@ gf119_disp_root_init(struct nv50_disp_root *root) * * ftp://download.nvidia.com/open-gpu-doc/gk104-disable-underflow-reporting/1/gk104-disable-underflow-reporting.txt */ - for (i = 0; i < disp->base.head.nr; i++) - nvkm_mask(device, 0x616308 + (i * 0x800), 0x00000111, 0x00000010); + list_for_each_entry(head, &disp->base.head, head) { + const u32 hoff = head->id * 0x800; + nvkm_mask(device, 0x616308 + hoff, 0x00000111, 0x00000010); + } return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv04.c index f535f43231e2..7f3e2554a83d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv04.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv04.c @@ -23,6 +23,7 @@ */ #define nv04_disp_root(p) container_of((p), struct nv04_disp_root, object) #include "priv.h" +#include "head.h" #include <core/client.h> @@ -36,73 +37,30 @@ struct nv04_disp_root { }; static int -nv04_disp_scanoutpos(struct nv04_disp_root *root, - void *data, u32 size, int head) -{ - struct nvkm_device *device = root->disp->engine.subdev.device; - struct nvkm_object *object = &root->object; - const u32 hoff = head * 0x2000; - union { - struct nv04_disp_scanoutpos_v0 v0; - } *args = data; - u32 line; - int ret = -ENOSYS; - - nvif_ioctl(object, "disp scanoutpos size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { - nvif_ioctl(object, "disp scanoutpos vers %d\n", - args->v0.version); - args->v0.vblanks = nvkm_rd32(device, 0x680800 + hoff) & 0xffff; - args->v0.vtotal = nvkm_rd32(device, 0x680804 + hoff) & 0xffff; - args->v0.vblanke = args->v0.vtotal - 1; - - args->v0.hblanks = nvkm_rd32(device, 0x680820 + hoff) & 0xffff; - args->v0.htotal = nvkm_rd32(device, 0x680824 + hoff) & 0xffff; - args->v0.hblanke = args->v0.htotal - 1; - - /* - * If output is vga instead of digital then vtotal/htotal is - * invalid so we have to give up and trigger the timestamping - * fallback in the drm core. - */ - if (!args->v0.vtotal || !args->v0.htotal) - return -ENOTSUPP; - - args->v0.time[0] = ktime_to_ns(ktime_get()); - line = nvkm_rd32(device, 0x600868 + hoff); - args->v0.time[1] = ktime_to_ns(ktime_get()); - args->v0.hline = (line & 0xffff0000) >> 16; - args->v0.vline = (line & 0x0000ffff); - } else - return ret; - - return 0; -} - -static int nv04_disp_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) { struct nv04_disp_root *root = nv04_disp_root(object); union { struct nv04_disp_mthd_v0 v0; } *args = data; - int head, ret = -ENOSYS; + struct nvkm_head *head; + int id, ret = -ENOSYS; nvif_ioctl(object, "disp mthd size %d\n", size); if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { nvif_ioctl(object, "disp mthd vers %d mthd %02x head %d\n", args->v0.version, args->v0.method, args->v0.head); mthd = args->v0.method; - head = args->v0.head; + id = args->v0.head; } else return ret; - if (head < 0 || head >= 2) + if (!(head = nvkm_head_find(root->disp, id))) return -ENXIO; switch (mthd) { case NV04_DISP_SCANOUTPOS: - return nv04_disp_scanoutpos(root, data, size, head); + return nvkm_head_mthd_scanoutpos(object, head, data, size); default: break; } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c index e70dc6a9ff7d..1208524aae14 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c @@ -23,6 +23,9 @@ */ #include "rootnv50.h" #include "dmacnv50.h" +#include "dp.h" +#include "head.h" +#include "ior.h" #include <core/client.h> #include <core/ramht.h> @@ -32,40 +35,6 @@ #include <nvif/cl5070.h> #include <nvif/unpack.h> -int -nv50_disp_root_scanoutpos(NV50_DISP_MTHD_V0) -{ - struct nvkm_device *device = disp->base.engine.subdev.device; - const u32 blanke = nvkm_rd32(device, 0x610aec + (head * 0x540)); - const u32 blanks = nvkm_rd32(device, 0x610af4 + (head * 0x540)); - const u32 total = nvkm_rd32(device, 0x610afc + (head * 0x540)); - union { - struct nv50_disp_scanoutpos_v0 v0; - } *args = data; - int ret = -ENOSYS; - - nvif_ioctl(object, "disp scanoutpos size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { - nvif_ioctl(object, "disp scanoutpos vers %d\n", - args->v0.version); - args->v0.vblanke = (blanke & 0xffff0000) >> 16; - args->v0.hblanke = (blanke & 0x0000ffff); - args->v0.vblanks = (blanks & 0xffff0000) >> 16; - args->v0.hblanks = (blanks & 0x0000ffff); - args->v0.vtotal = ( total & 0xffff0000) >> 16; - args->v0.htotal = ( total & 0x0000ffff); - args->v0.time[0] = ktime_to_ns(ktime_get()); - args->v0.vline = /* vline read locks hline */ - nvkm_rd32(device, 0x616340 + (head * 0x800)) & 0xffff; - args->v0.time[1] = ktime_to_ns(ktime_get()); - args->v0.hline = - nvkm_rd32(device, 0x616344 + (head * 0x800)) & 0xffff; - } else - return ret; - - return 0; -} - static int nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) { @@ -75,11 +44,10 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) } *args = data; struct nv50_disp_root *root = nv50_disp_root(object); struct nv50_disp *disp = root->disp; - const struct nv50_disp_func *func = disp->func; - struct nvkm_output *outp = NULL; - struct nvkm_output *temp; + struct nvkm_outp *temp, *outp = NULL; + struct nvkm_head *head; u16 type, mask = 0; - int head, ret = -ENOSYS; + int hidx, ret = -ENOSYS; if (mthd != NV50_DISP_MTHD) return -EINVAL; @@ -89,7 +57,7 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) nvif_ioctl(object, "disp mthd vers %d mthd %02x head %d\n", args->v0.version, args->v0.method, args->v0.head); mthd = args->v0.method; - head = args->v0.head; + hidx = args->v0.head; } else if (!(ret = nvif_unpack(ret, &data, &size, args->v1, 1, 1, true))) { nvif_ioctl(object, "disp mthd vers %d mthd %02x " @@ -99,11 +67,11 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) mthd = args->v1.method; type = args->v1.hasht; mask = args->v1.hashm; - head = ffs((mask >> 8) & 0x0f) - 1; + hidx = ffs((mask >> 8) & 0x0f) - 1; } else return ret; - if (head < 0 || head >= disp->base.head.nr) + if (!(head = nvkm_head_find(&disp->base, hidx))) return -ENXIO; if (mask) { @@ -119,27 +87,126 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) } switch (mthd) { - case NV50_DISP_SCANOUTPOS: - return func->head.scanoutpos(object, disp, data, size, head); + case NV50_DISP_SCANOUTPOS: { + return nvkm_head_mthd_scanoutpos(object, head, data, size); + } default: break; } switch (mthd * !!outp) { - case NV50_DISP_MTHD_V1_DAC_PWR: - return func->dac.power(object, disp, data, size, head, outp); - case NV50_DISP_MTHD_V1_DAC_LOAD: - return func->dac.sense(object, disp, data, size, head, outp); - case NV50_DISP_MTHD_V1_SOR_PWR: - return func->sor.power(object, disp, data, size, head, outp); - case NV50_DISP_MTHD_V1_SOR_HDA_ELD: - if (!func->sor.hda_eld) + case NV50_DISP_MTHD_V1_ACQUIRE: { + union { + struct nv50_disp_acquire_v0 v0; + } *args = data; + int ret = -ENOSYS; + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER); + if (ret == 0) { + args->v0.or = outp->ior->id; + args->v0.link = outp->ior->asy.link; + } + } + return ret; + } + break; + case NV50_DISP_MTHD_V1_RELEASE: + nvkm_outp_release(outp, NVKM_OUTP_USER); + return 0; + case NV50_DISP_MTHD_V1_DAC_LOAD: { + union { + struct nv50_disp_dac_load_v0 v0; + } *args = data; + int ret = -ENOSYS; + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + if (args->v0.data & 0xfff00000) + return -EINVAL; + ret = nvkm_outp_acquire(outp, NVKM_OUTP_PRIV); + if (ret) + return ret; + ret = outp->ior->func->sense(outp->ior, args->v0.data); + nvkm_outp_release(outp, NVKM_OUTP_PRIV); + if (ret < 0) + return ret; + args->v0.load = ret; + return 0; + } else + return ret; + } + break; + case NV50_DISP_MTHD_V1_SOR_HDA_ELD: { + union { + struct nv50_disp_sor_hda_eld_v0 v0; + } *args = data; + struct nvkm_ior *ior = outp->ior; + int ret = -ENOSYS; + + nvif_ioctl(object, "disp sor hda eld size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { + nvif_ioctl(object, "disp sor hda eld vers %d\n", + args->v0.version); + if (size > 0x60) + return -E2BIG; + } else + return ret; + + if (!ior->func->hda.hpd) return -ENODEV; - return func->sor.hda_eld(object, disp, data, size, head, outp); - case NV50_DISP_MTHD_V1_SOR_HDMI_PWR: - if (!func->sor.hdmi) + + if (size && args->v0.data[0]) { + if (outp->info.type == DCB_OUTPUT_DP) + ior->func->dp.audio(ior, hidx, true); + ior->func->hda.hpd(ior, hidx, true); + ior->func->hda.eld(ior, data, size); + } else { + if (outp->info.type == DCB_OUTPUT_DP) + ior->func->dp.audio(ior, hidx, false); + ior->func->hda.hpd(ior, hidx, false); + } + + return 0; + } + break; + case NV50_DISP_MTHD_V1_SOR_HDMI_PWR: { + union { + struct nv50_disp_sor_hdmi_pwr_v0 v0; + } *args = data; + u8 *vendor, vendor_size; + u8 *avi, avi_size; + int ret = -ENOSYS; + + nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { + nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " + "max_ac_packet %d rekey %d\n", + args->v0.version, args->v0.state, + args->v0.max_ac_packet, args->v0.rekey); + if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f) + return -EINVAL; + if ((args->v0.avi_infoframe_length + + args->v0.vendor_infoframe_length) > size) + return -EINVAL; + else + if ((args->v0.avi_infoframe_length + + args->v0.vendor_infoframe_length) < size) + return -E2BIG; + avi = data; + avi_size = args->v0.avi_infoframe_length; + vendor = avi + avi_size; + vendor_size = args->v0.vendor_infoframe_length; + } else + return ret; + + if (!outp->ior->func->hdmi.ctrl) return -ENODEV; - return func->sor.hdmi(object, disp, data, size, head, outp); + + outp->ior->func->hdmi.ctrl(outp->ior, hidx, args->v0.state, + args->v0.max_ac_packet, + args->v0.rekey, avi, avi_size, + vendor, vendor_size); + return 0; + } + break; case NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT: { union { struct nv50_disp_sor_lvds_script_v0 v0; @@ -156,32 +223,8 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) return ret; } break; - case NV50_DISP_MTHD_V1_SOR_DP_PWR: { - struct nvkm_output_dp *outpdp = nvkm_output_dp(outp); - union { - struct nv50_disp_sor_dp_pwr_v0 v0; - } *args = data; - int ret = -ENOSYS; - nvif_ioctl(object, "disp sor dp pwr size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { - nvif_ioctl(object, "disp sor dp pwr vers %d state %d\n", - args->v0.version, args->v0.state); - if (args->v0.state == 0) { - nvkm_notify_put(&outpdp->irq); - outpdp->func->lnk_pwr(outpdp, 0); - atomic_set(&outpdp->lt.done, 0); - return 0; - } else - if (args->v0.state != 0) { - nvkm_output_dp_train(&outpdp->base, 0); - return 0; - } - } else - return ret; - } - break; case NV50_DISP_MTHD_V1_SOR_DP_MST_LINK: { - struct nvkm_output_dp *outpdp = nvkm_output_dp(outp); + struct nvkm_dp *dp = nvkm_dp(outp); union { struct nv50_disp_sor_dp_mst_link_v0 v0; } *args = data; @@ -190,18 +233,13 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { nvif_ioctl(object, "disp sor dp mst link vers %d state %d\n", args->v0.version, args->v0.state); - if (outpdp->lt.mst != !!args->v0.state) { - outpdp->lt.mst = !!args->v0.state; - atomic_set(&outpdp->lt.done, 0); - nvkm_output_dp_train(&outpdp->base, 0); - } + dp->lt.mst = !!args->v0.state; return 0; } else return ret; } break; case NV50_DISP_MTHD_V1_SOR_DP_MST_VCPI: { - struct nvkm_output_dp *outpdp = nvkm_output_dp(outp); union { struct nv50_disp_sor_dp_mst_vcpi_v0 v0; } *args = data; @@ -213,20 +251,18 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) args->v0.version, args->v0.start_slot, args->v0.num_slots, args->v0.pbn, args->v0.aligned_pbn); - if (!outpdp->func->vcpi) + if (!outp->ior->func->dp.vcpi) return -ENODEV; - outpdp->func->vcpi(outpdp, head, args->v0.start_slot, - args->v0.num_slots, args->v0.pbn, - args->v0.aligned_pbn); + outp->ior->func->dp.vcpi(outp->ior, hidx, + args->v0.start_slot, + args->v0.num_slots, + args->v0.pbn, + args->v0.aligned_pbn); return 0; } else return ret; } break; - case NV50_DISP_MTHD_V1_PIOR_PWR: - if (!func->pior.power) - return -ENODEV; - return func->pior.power(object, disp, data, size, head, outp); default: break; } @@ -291,7 +327,21 @@ static int nv50_disp_root_init_(struct nvkm_object *object) { struct nv50_disp_root *root = nv50_disp_root(object); - return root->func->init(root); + struct nvkm_ior *ior; + int ret; + + ret = root->func->init(root); + if (ret) + return ret; + + /* Set 'normal' (ie. when it's attached to a head) state for + * each output resource to 'fully enabled'. + */ + list_for_each_entry(ior, &root->disp->base.ior, head) { + ior->func->power(ior, true, true, true, true, true); + } + + return 0; } static void * @@ -352,6 +402,7 @@ int nv50_disp_root_init(struct nv50_disp_root *root) { struct nv50_disp *disp = root->disp; + struct nvkm_head *head; struct nvkm_device *device = disp->base.engine.subdev.device; u32 tmp; int i; @@ -364,15 +415,15 @@ nv50_disp_root_init(struct nv50_disp_root *root) nvkm_wr32(device, 0x610184, tmp); /* ... CRTC caps */ - for (i = 0; i < disp->base.head.nr; i++) { - tmp = nvkm_rd32(device, 0x616100 + (i * 0x800)); - nvkm_wr32(device, 0x610190 + (i * 0x10), tmp); - tmp = nvkm_rd32(device, 0x616104 + (i * 0x800)); - nvkm_wr32(device, 0x610194 + (i * 0x10), tmp); - tmp = nvkm_rd32(device, 0x616108 + (i * 0x800)); - nvkm_wr32(device, 0x610198 + (i * 0x10), tmp); - tmp = nvkm_rd32(device, 0x61610c + (i * 0x800)); - nvkm_wr32(device, 0x61019c + (i * 0x10), tmp); + list_for_each_entry(head, &disp->base.head, head) { + tmp = nvkm_rd32(device, 0x616100 + (head->id * 0x800)); + nvkm_wr32(device, 0x610190 + (head->id * 0x10), tmp); + tmp = nvkm_rd32(device, 0x616104 + (head->id * 0x800)); + nvkm_wr32(device, 0x610194 + (head->id * 0x10), tmp); + tmp = nvkm_rd32(device, 0x616108 + (head->id * 0x800)); + nvkm_wr32(device, 0x610198 + (head->id * 0x10), tmp); + tmp = nvkm_rd32(device, 0x61610c + (head->id * 0x800)); + nvkm_wr32(device, 0x61019c + (head->id * 0x10), tmp); } /* ... DAC caps */ diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg84.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg84.c new file mode 100644 index 000000000000..f40b909b4ca2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg84.c @@ -0,0 +1,38 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * 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 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 HOLDER(S) OR AUTHOR(S) 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 "ior.h" + +static const struct nvkm_ior_func +g84_sor = { + .state = nv50_sor_state, + .power = nv50_sor_power, + .clock = nv50_sor_clock, + .hdmi = { + .ctrl = g84_hdmi_ctrl, + }, +}; + +int +g84_sor_new(struct nvkm_disp *disp, int id) +{ + return nv50_sor_new_(&g84_sor, disp, id); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c index 627b9ee1ddd2..49aeafde0031 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c @@ -21,58 +21,75 @@ * * Authors: Ben Skeggs */ -#include "nv50.h" -#include "outpdp.h" +#include "ior.h" #include <subdev/timer.h> -static inline u32 -g94_sor_soff(struct nvkm_output_dp *outp) +void +g94_sor_dp_watermark(struct nvkm_ior *sor, int head, u8 watermark) { - return (ffs(outp->base.info.or) - 1) * 0x800; + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 loff = nv50_sor_link(sor); + nvkm_mask(device, 0x61c128 + loff, 0x0000003f, watermark); } -static inline u32 -g94_sor_loff(struct nvkm_output_dp *outp) +void +g94_sor_dp_activesym(struct nvkm_ior *sor, int head, + u8 TU, u8 VTUa, u8 VTUf, u8 VTUi) { - return g94_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80; + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 loff = nv50_sor_link(sor); + nvkm_mask(device, 0x61c10c + loff, 0x000001fc, TU << 2); + nvkm_mask(device, 0x61c128 + loff, 0x010f7f00, VTUa << 24 | + VTUf << 16 | + VTUi << 8); } -/******************************************************************************* - * DisplayPort - ******************************************************************************/ -u32 -g94_sor_dp_lane_map(struct nvkm_device *device, u8 lane) +void +g94_sor_dp_audio_sym(struct nvkm_ior *sor, int head, u16 h, u32 v) { - static const u8 gm100[] = { 0, 8, 16, 24 }; - static const u8 mcp89[] = { 24, 16, 8, 0 }; /* thanks, apple.. */ - static const u8 g94[] = { 16, 8, 0, 24 }; - if (device->chipset >= 0x110) - return gm100[lane]; - if (device->chipset == 0xaf) - return mcp89[lane]; - return g94[lane]; + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); + nvkm_mask(device, 0x61c1e8 + soff, 0x0000ffff, h); + nvkm_mask(device, 0x61c1ec + soff, 0x00ffffff, v); } -static int -g94_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern) +void +g94_sor_dp_drive(struct nvkm_ior *sor, int ln, int pc, int dc, int pe, int pu) { - struct nvkm_device *device = outp->base.disp->engine.subdev.device; - const u32 loff = g94_sor_loff(outp); + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 loff = nv50_sor_link(sor); + const u32 shift = sor->func->dp.lanes[ln] * 8; + u32 data[3]; + + data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift); + data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift); + data[2] = nvkm_rd32(device, 0x61c130 + loff); + if ((data[2] & 0x0000ff00) < (pu << 8) || ln == 0) + data[2] = (data[2] & ~0x0000ff00) | (pu << 8); + nvkm_wr32(device, 0x61c118 + loff, data[0] | (dc << shift)); + nvkm_wr32(device, 0x61c120 + loff, data[1] | (pe << shift)); + nvkm_wr32(device, 0x61c130 + loff, data[2]); +} + +void +g94_sor_dp_pattern(struct nvkm_ior *sor, int pattern) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 loff = nv50_sor_link(sor); nvkm_mask(device, 0x61c10c + loff, 0x0f000000, pattern << 24); - return 0; } -int -g94_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr) +void +g94_sor_dp_power(struct nvkm_ior *sor, int nr) { - struct nvkm_device *device = outp->base.disp->engine.subdev.device; - const u32 soff = g94_sor_soff(outp); - const u32 loff = g94_sor_loff(outp); + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); + const u32 loff = nv50_sor_link(sor); u32 mask = 0, i; for (i = 0; i < nr; i++) - mask |= 1 << (g94_sor_dp_lane_map(device, i) >> 3); + mask |= 1 << sor->func->dp.lanes[i]; nvkm_mask(device, 0x61c130 + loff, 0x0000000f, mask); nvkm_mask(device, 0x61c034 + soff, 0x80000000, 0x80000000); @@ -80,22 +97,21 @@ g94_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr) if (!(nvkm_rd32(device, 0x61c034 + soff) & 0x80000000)) break; ); - return 0; } -static int -g94_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef) +int +g94_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux) { - struct nvkm_device *device = outp->base.disp->engine.subdev.device; - const u32 soff = g94_sor_soff(outp); - const u32 loff = g94_sor_loff(outp); + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); + const u32 loff = nv50_sor_link(sor); u32 dpctrl = 0x00000000; u32 clksor = 0x00000000; - dpctrl |= ((1 << nr) - 1) << 16; - if (ef) + dpctrl |= ((1 << sor->dp.nr) - 1) << 16; + if (sor->dp.ef) dpctrl |= 0x00004000; - if (bw > 0x06) + if (sor->dp.bw > 0x06) clksor |= 0x00040000; nvkm_mask(device, 0x614300 + soff, 0x000c0000, clksor); @@ -103,51 +119,165 @@ g94_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef) return 0; } -static int -g94_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc) +static bool +g94_sor_war_needed(struct nvkm_ior *sor) { - struct nvkm_device *device = outp->base.disp->engine.subdev.device; - struct nvkm_bios *bios = device->bios; - const u32 shift = g94_sor_dp_lane_map(device, ln); - const u32 loff = g94_sor_loff(outp); - u32 addr, data[3]; - u8 ver, hdr, cnt, len; - struct nvbios_dpout info; - struct nvbios_dpcfg ocfg; - - addr = nvbios_dpout_match(bios, outp->base.info.hasht, - outp->base.info.hashm, - &ver, &hdr, &cnt, &len, &info); - if (!addr) - return -ENODEV; - - addr = nvbios_dpcfg_match(bios, addr, 0, vs, pe, - &ver, &hdr, &cnt, &len, &ocfg); - if (!addr) - return -EINVAL; + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); + if (sor->asy.proto == TMDS) { + switch (nvkm_rd32(device, 0x614300 + soff) & 0x00030000) { + case 0x00000000: + case 0x00030000: + return true; + default: + break; + } + } + return false; +} - data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift); - data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift); - data[2] = nvkm_rd32(device, 0x61c130 + loff); - if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0) - data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8); - nvkm_wr32(device, 0x61c118 + loff, data[0] | (ocfg.dc << shift)); - nvkm_wr32(device, 0x61c120 + loff, data[1] | (ocfg.pe << shift)); - nvkm_wr32(device, 0x61c130 + loff, data[2]); - return 0; +static void +g94_sor_war_update_sppll1(struct nvkm_disp *disp) +{ + struct nvkm_device *device = disp->engine.subdev.device; + struct nvkm_ior *ior; + bool used = false; + u32 clksor; + + list_for_each_entry(ior, &disp->ior, head) { + if (ior->type != SOR) + continue; + + clksor = nvkm_rd32(device, 0x614300 + nv50_ior_base(ior)); + switch (clksor & 0x03000000) { + case 0x02000000: + case 0x03000000: + used = true; + break; + default: + break; + } + } + + if (used) + return; + + nvkm_mask(device, 0x00e840, 0x80000000, 0x00000000); +} + +static void +g94_sor_war_3(struct nvkm_ior *sor) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); + u32 sorpwr; + + if (!g94_sor_war_needed(sor)) + return; + + sorpwr = nvkm_rd32(device, 0x61c004 + soff); + if (sorpwr & 0x00000001) { + u32 seqctl = nvkm_rd32(device, 0x61c030 + soff); + u32 pd_pc = (seqctl & 0x00000f00) >> 8; + u32 pu_pc = seqctl & 0x0000000f; + + nvkm_wr32(device, 0x61c040 + soff + pd_pc * 4, 0x1f008000); + + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000)) + break; + ); + nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000000); + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000)) + break; + ); + + nvkm_wr32(device, 0x61c040 + soff + pd_pc * 4, 0x00002000); + nvkm_wr32(device, 0x61c040 + soff + pu_pc * 4, 0x1f000000); + } + + nvkm_mask(device, 0x61c10c + soff, 0x00000001, 0x00000000); + nvkm_mask(device, 0x614300 + soff, 0x03000000, 0x00000000); + + if (sorpwr & 0x00000001) { + nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000001); + } + + g94_sor_war_update_sppll1(sor->disp); +} + +static void +g94_sor_war_2(struct nvkm_ior *sor) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); + + if (!g94_sor_war_needed(sor)) + return; + + nvkm_mask(device, 0x00e840, 0x80000000, 0x80000000); + nvkm_mask(device, 0x614300 + soff, 0x03000000, 0x03000000); + nvkm_mask(device, 0x61c10c + soff, 0x00000001, 0x00000001); + + nvkm_mask(device, 0x61c00c + soff, 0x0f000000, 0x00000000); + nvkm_mask(device, 0x61c008 + soff, 0xff000000, 0x14000000); + nvkm_usec(device, 400, NVKM_DELAY); + nvkm_mask(device, 0x61c008 + soff, 0xff000000, 0x00000000); + nvkm_mask(device, 0x61c00c + soff, 0x0f000000, 0x01000000); + + if (nvkm_rd32(device, 0x61c004 + soff) & 0x00000001) { + u32 seqctl = nvkm_rd32(device, 0x61c030 + soff); + u32 pu_pc = seqctl & 0x0000000f; + nvkm_wr32(device, 0x61c040 + soff + pu_pc * 4, 0x1f008000); + } +} + +void +g94_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 coff = sor->id * 8 + (state == &sor->arm) * 4; + u32 ctrl = nvkm_rd32(device, 0x610794 + coff); + + state->proto_evo = (ctrl & 0x00000f00) >> 8; + switch (state->proto_evo) { + case 0: state->proto = LVDS; state->link = 1; break; + case 1: state->proto = TMDS; state->link = 1; break; + case 2: state->proto = TMDS; state->link = 2; break; + case 5: state->proto = TMDS; state->link = 3; break; + case 8: state->proto = DP; state->link = 1; break; + case 9: state->proto = DP; state->link = 2; break; + default: + state->proto = UNKNOWN; + break; + } + + state->head = ctrl & 0x00000003; + nv50_pior_depth(sor, state, ctrl); } -static const struct nvkm_output_dp_func -g94_sor_dp_func = { - .pattern = g94_sor_dp_pattern, - .lnk_pwr = g94_sor_dp_lnk_pwr, - .lnk_ctl = g94_sor_dp_lnk_ctl, - .drv_ctl = g94_sor_dp_drv_ctl, +static const struct nvkm_ior_func +g94_sor = { + .state = g94_sor_state, + .power = nv50_sor_power, + .clock = nv50_sor_clock, + .war_2 = g94_sor_war_2, + .war_3 = g94_sor_war_3, + .dp = { + .lanes = { 2, 1, 0, 3}, + .links = g94_sor_dp_links, + .power = g94_sor_dp_power, + .pattern = g94_sor_dp_pattern, + .drive = g94_sor_dp_drive, + .audio_sym = g94_sor_dp_audio_sym, + .activesym = g94_sor_dp_activesym, + .watermark = g94_sor_dp_watermark, + }, }; int -g94_sor_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, - struct nvkm_output **poutp) +g94_sor_new(struct nvkm_disp *disp, int id) { - return nvkm_output_dp_new_(&g94_sor_dp_func, disp, index, dcbE, poutp); + return nv50_sor_new_(&g94_sor, disp, id); } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c index 6ffdaa65aa77..a2978a37b4f3 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c @@ -21,44 +21,94 @@ * * Authors: Ben Skeggs */ -#include "nv50.h" -#include "outpdp.h" +#include "ior.h" -static inline u32 -gf119_sor_soff(struct nvkm_output_dp *outp) +#include <subdev/timer.h> + +void +gf119_sor_dp_watermark(struct nvkm_ior *sor, int head, u8 watermark) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 hoff = head * 0x800; + nvkm_mask(device, 0x616610 + hoff, 0x0800003f, 0x08000000 | watermark); +} + +void +gf119_sor_dp_audio_sym(struct nvkm_ior *sor, int head, u16 h, u32 v) { - return (ffs(outp->base.info.or) - 1) * 0x800; + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 hoff = head * 0x800; + nvkm_mask(device, 0x616620 + hoff, 0x0000ffff, h); + nvkm_mask(device, 0x616624 + hoff, 0x00ffffff, v); } -static inline u32 -gf119_sor_loff(struct nvkm_output_dp *outp) +void +gf119_sor_dp_audio(struct nvkm_ior *sor, int head, bool enable) { - return gf119_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80; + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 hoff = 0x800 * head; + const u32 data = 0x80000000 | (0x00000001 * enable); + const u32 mask = 0x8000000d; + nvkm_mask(device, 0x616618 + hoff, mask, data); + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x616618 + hoff) & 0x80000000)) + break; + ); +} + +void +gf119_sor_dp_vcpi(struct nvkm_ior *sor, int head, + u8 slot, u8 slot_nr, u16 pbn, u16 aligned) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 hoff = head * 0x800; + + nvkm_mask(device, 0x616588 + hoff, 0x00003f3f, (slot_nr << 8) | slot); + nvkm_mask(device, 0x61658c + hoff, 0xffffffff, (aligned << 16) | pbn); +} + +void +gf119_sor_dp_drive(struct nvkm_ior *sor, int ln, int pc, int dc, int pe, int pu) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 loff = nv50_sor_link(sor); + const u32 shift = sor->func->dp.lanes[ln] * 8; + u32 data[4]; + + data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift); + data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift); + data[2] = nvkm_rd32(device, 0x61c130 + loff); + if ((data[2] & 0x0000ff00) < (pu << 8) || ln == 0) + data[2] = (data[2] & ~0x0000ff00) | (pu << 8); + nvkm_wr32(device, 0x61c118 + loff, data[0] | (dc << shift)); + nvkm_wr32(device, 0x61c120 + loff, data[1] | (pe << shift)); + nvkm_wr32(device, 0x61c130 + loff, data[2]); + data[3] = nvkm_rd32(device, 0x61c13c + loff) & ~(0x000000ff << shift); + nvkm_wr32(device, 0x61c13c + loff, data[3] | (pc << shift)); } -static int -gf119_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern) +void +gf119_sor_dp_pattern(struct nvkm_ior *sor, int pattern) { - struct nvkm_device *device = outp->base.disp->engine.subdev.device; - const u32 soff = gf119_sor_soff(outp); + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); nvkm_mask(device, 0x61c110 + soff, 0x0f0f0f0f, 0x01010101 * pattern); - return 0; } int -gf119_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef) +gf119_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux) { - struct nvkm_device *device = outp->base.disp->engine.subdev.device; - const u32 soff = gf119_sor_soff(outp); - const u32 loff = gf119_sor_loff(outp); + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); + const u32 loff = nv50_sor_link(sor); u32 dpctrl = 0x00000000; u32 clksor = 0x00000000; - clksor |= bw << 18; - dpctrl |= ((1 << nr) - 1) << 16; - if (outp->lt.mst) + clksor |= sor->dp.bw << 18; + dpctrl |= ((1 << sor->dp.nr) - 1) << 16; + if (sor->dp.mst) dpctrl |= 0x40000000; - if (ef) + if (sor->dp.ef) dpctrl |= 0x00004000; nvkm_mask(device, 0x612300 + soff, 0x007c0000, clksor); @@ -66,66 +116,77 @@ gf119_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef) return 0; } -int -gf119_sor_dp_drv_ctl(struct nvkm_output_dp *outp, - int ln, int vs, int pe, int pc) +void +gf119_sor_clock(struct nvkm_ior *sor) { - struct nvkm_device *device = outp->base.disp->engine.subdev.device; - struct nvkm_bios *bios = device->bios; - const u32 shift = g94_sor_dp_lane_map(device, ln); - const u32 loff = gf119_sor_loff(outp); - u32 addr, data[4]; - u8 ver, hdr, cnt, len; - struct nvbios_dpout info; - struct nvbios_dpcfg ocfg; - - addr = nvbios_dpout_match(bios, outp->base.info.hasht, - outp->base.info.hashm, - &ver, &hdr, &cnt, &len, &info); - if (!addr) - return -ENODEV; - - addr = nvbios_dpcfg_match(bios, addr, pc, vs, pe, - &ver, &hdr, &cnt, &len, &ocfg); - if (!addr) - return -EINVAL; - - data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift); - data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift); - data[2] = nvkm_rd32(device, 0x61c130 + loff); - if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0) - data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8); - nvkm_wr32(device, 0x61c118 + loff, data[0] | (ocfg.dc << shift)); - nvkm_wr32(device, 0x61c120 + loff, data[1] | (ocfg.pe << shift)); - nvkm_wr32(device, 0x61c130 + loff, data[2]); - data[3] = nvkm_rd32(device, 0x61c13c + loff) & ~(0x000000ff << shift); - nvkm_wr32(device, 0x61c13c + loff, data[3] | (ocfg.pc << shift)); - return 0; + struct nvkm_device *device = sor->disp->engine.subdev.device; + const int div = sor->asy.link == 3; + const u32 soff = nv50_ior_base(sor); + if (sor->asy.proto == TMDS) { + /* NFI why, but this sets DP_LINK_BW_2_7 when using TMDS. */ + nvkm_mask(device, 0x612300 + soff, 0x007c0000, 0x0a << 18); + } + nvkm_mask(device, 0x612300 + soff, 0x00000707, (div << 8) | div); } void -gf119_sor_dp_vcpi(struct nvkm_output_dp *outp, int head, u8 slot, - u8 slot_nr, u16 pbn, u16 aligned) +gf119_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state) { - struct nvkm_device *device = outp->base.disp->engine.subdev.device; - const u32 hoff = head * 0x800; + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 coff = (state == &sor->asy) * 0x20000 + sor->id * 0x20; + u32 ctrl = nvkm_rd32(device, 0x640200 + coff); - nvkm_mask(device, 0x616588 + hoff, 0x00003f3f, (slot_nr << 8) | slot); - nvkm_mask(device, 0x61658c + hoff, 0xffffffff, (aligned << 16) | pbn); + state->proto_evo = (ctrl & 0x00000f00) >> 8; + switch (state->proto_evo) { + case 0: state->proto = LVDS; state->link = 1; break; + case 1: state->proto = TMDS; state->link = 1; break; + case 2: state->proto = TMDS; state->link = 2; break; + case 5: state->proto = TMDS; state->link = 3; break; + case 8: state->proto = DP; state->link = 1; break; + case 9: state->proto = DP; state->link = 2; break; + default: + state->proto = UNKNOWN; + break; + } + + state->head = ctrl & 0x0000000f; +} + +int +gf119_sor_new_(const struct nvkm_ior_func *func, struct nvkm_disp *disp, int id) +{ + struct nvkm_device *device = disp->engine.subdev.device; + if (!(nvkm_rd32(device, 0x612004) & (0x00000100 << id))) + return 0; + return nvkm_ior_new_(func, disp, SOR, id); } -static const struct nvkm_output_dp_func -gf119_sor_dp_func = { - .pattern = gf119_sor_dp_pattern, - .lnk_pwr = g94_sor_dp_lnk_pwr, - .lnk_ctl = gf119_sor_dp_lnk_ctl, - .drv_ctl = gf119_sor_dp_drv_ctl, - .vcpi = gf119_sor_dp_vcpi, +static const struct nvkm_ior_func +gf119_sor = { + .state = gf119_sor_state, + .power = nv50_sor_power, + .clock = gf119_sor_clock, + .hdmi = { + .ctrl = gf119_hdmi_ctrl, + }, + .dp = { + .lanes = { 2, 1, 0, 3 }, + .links = gf119_sor_dp_links, + .power = g94_sor_dp_power, + .pattern = gf119_sor_dp_pattern, + .vcpi = gf119_sor_dp_vcpi, + .audio = gf119_sor_dp_audio, + .audio_sym = gf119_sor_dp_audio_sym, + .watermark = gf119_sor_dp_watermark, + }, + .hda = { + .hpd = gf119_hda_hpd, + .eld = gf119_hda_eld, + }, }; int -gf119_sor_dp_new(struct nvkm_disp *disp, int index, - struct dcb_output *dcbE, struct nvkm_output **poutp) +gf119_sor_new(struct nvkm_disp *disp, int id) { - return nvkm_output_dp_new_(&gf119_sor_dp_func, disp, index, dcbE, poutp); + return gf119_sor_new_(&gf119_sor, disp, id); } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgk104.c new file mode 100644 index 000000000000..a1547bdf490b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgk104.c @@ -0,0 +1,53 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * 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 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 HOLDER(S) OR AUTHOR(S) 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 "ior.h" + +static const struct nvkm_ior_func +gk104_sor = { + .state = gf119_sor_state, + .power = nv50_sor_power, + .clock = gf119_sor_clock, + .hdmi = { + .ctrl = gk104_hdmi_ctrl, + }, + .dp = { + .lanes = { 2, 1, 0, 3 }, + .links = gf119_sor_dp_links, + .power = g94_sor_dp_power, + .pattern = gf119_sor_dp_pattern, + .drive = gf119_sor_dp_drive, + .vcpi = gf119_sor_dp_vcpi, + .audio = gf119_sor_dp_audio, + .audio_sym = gf119_sor_dp_audio_sym, + .watermark = gf119_sor_dp_watermark, + }, + .hda = { + .hpd = gf119_hda_hpd, + .eld = gf119_hda_eld, + }, +}; + +int +gk104_sor_new(struct nvkm_disp *disp, int id) +{ + return gf119_sor_new_(&gk104_sor, disp, id); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c index 4cf8ad4d18ab..60230957d82b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c @@ -21,34 +21,47 @@ * * Authors: Ben Skeggs <bskeggs@redhat.com> */ -#include "nv50.h" -#include "outpdp.h" +#include "ior.h" -int -gm107_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern) +void +gm107_sor_dp_pattern(struct nvkm_ior *sor, int pattern) { - struct nvkm_device *device = outp->base.disp->engine.subdev.device; - const u32 soff = outp->base.or * 0x800; + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); const u32 data = 0x01010101 * pattern; - if (outp->base.info.sorconf.link & 1) + if (sor->asy.link & 1) nvkm_mask(device, 0x61c110 + soff, 0x0f0f0f0f, data); else nvkm_mask(device, 0x61c12c + soff, 0x0f0f0f0f, data); - return 0; } -static const struct nvkm_output_dp_func -gm107_sor_dp_func = { - .pattern = gm107_sor_dp_pattern, - .lnk_pwr = g94_sor_dp_lnk_pwr, - .lnk_ctl = gf119_sor_dp_lnk_ctl, - .drv_ctl = gf119_sor_dp_drv_ctl, - .vcpi = gf119_sor_dp_vcpi, +static const struct nvkm_ior_func +gm107_sor = { + .state = gf119_sor_state, + .power = nv50_sor_power, + .clock = gf119_sor_clock, + .hdmi = { + .ctrl = gk104_hdmi_ctrl, + }, + .dp = { + .lanes = { 0, 1, 2, 3 }, + .links = gf119_sor_dp_links, + .power = g94_sor_dp_power, + .pattern = gm107_sor_dp_pattern, + .drive = gf119_sor_dp_drive, + .vcpi = gf119_sor_dp_vcpi, + .audio = gf119_sor_dp_audio, + .audio_sym = gf119_sor_dp_audio_sym, + .watermark = gf119_sor_dp_watermark, + }, + .hda = { + .hpd = gf119_hda_hpd, + .eld = gf119_hda_eld, + }, }; int -gm107_sor_dp_new(struct nvkm_disp *disp, int index, - struct dcb_output *dcbE, struct nvkm_output **poutp) +gm107_sor_new(struct nvkm_disp *disp, int id) { - return nvkm_output_dp_new_(&gm107_sor_dp_func, disp, index, dcbE, poutp); + return gf119_sor_new_(&gm107_sor, disp, id); } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c index 81b788fa61be..f9b8107aa2a2 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c @@ -21,111 +21,104 @@ * * Authors: Ben Skeggs */ -#include "nv50.h" -#include "outpdp.h" +#include "ior.h" -#include <subdev/timer.h> - -static inline u32 -gm200_sor_soff(struct nvkm_output_dp *outp) -{ - return (ffs(outp->base.info.or) - 1) * 0x800; -} - -static inline u32 -gm200_sor_loff(struct nvkm_output_dp *outp) +static void +gm200_sor_dp_drive(struct nvkm_ior *sor, int ln, int pc, int dc, int pe, int pu) { - return gm200_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80; -} + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 loff = nv50_sor_link(sor); + const u32 shift = sor->func->dp.lanes[ln] * 8; + u32 data[4]; -void -gm200_sor_magic(struct nvkm_output *outp) -{ - struct nvkm_device *device = outp->disp->engine.subdev.device; - const u32 soff = outp->or * 0x100; - const u32 data = outp->or + 1; - if (outp->info.sorconf.link & 1) - nvkm_mask(device, 0x612308 + soff, 0x0000001f, 0x00000000 | data); - if (outp->info.sorconf.link & 2) - nvkm_mask(device, 0x612388 + soff, 0x0000001f, 0x00000010 | data); -} + pu &= 0x0f; -static inline u32 -gm200_sor_dp_lane_map(struct nvkm_device *device, u8 lane) -{ - return lane * 0x08; + data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift); + data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift); + data[2] = nvkm_rd32(device, 0x61c130 + loff); + if ((data[2] & 0x00000f00) < (pu << 8) || ln == 0) + data[2] = (data[2] & ~0x00000f00) | (pu << 8); + nvkm_wr32(device, 0x61c118 + loff, data[0] | (dc << shift)); + nvkm_wr32(device, 0x61c120 + loff, data[1] | (pe << shift)); + nvkm_wr32(device, 0x61c130 + loff, data[2]); + data[3] = nvkm_rd32(device, 0x61c13c + loff) & ~(0x000000ff << shift); + nvkm_wr32(device, 0x61c13c + loff, data[3] | (pc << shift)); } -static int -gm200_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr) +static void +gm200_sor_route_set(struct nvkm_outp *outp, struct nvkm_ior *ior) { - struct nvkm_device *device = outp->base.disp->engine.subdev.device; - const u32 soff = gm200_sor_soff(outp); - const u32 loff = gm200_sor_loff(outp); - u32 mask = 0, i; + struct nvkm_device *device = outp->disp->engine.subdev.device; + const u32 moff = __ffs(outp->info.or) * 0x100; + const u32 sor = ior ? ior->id + 1 : 0; + u32 link = ior ? (ior->asy.link == 2) : 0; - for (i = 0; i < nr; i++) - mask |= 1 << (gm200_sor_dp_lane_map(device, i) >> 3); + if (outp->info.sorconf.link & 1) { + nvkm_mask(device, 0x612308 + moff, 0x0000001f, link << 4 | sor); + link++; + } - nvkm_mask(device, 0x61c130 + loff, 0x0000000f, mask); - nvkm_mask(device, 0x61c034 + soff, 0x80000000, 0x80000000); - nvkm_msec(device, 2000, - if (!(nvkm_rd32(device, 0x61c034 + soff) & 0x80000000)) - break; - ); - return 0; + if (outp->info.sorconf.link & 2) + nvkm_mask(device, 0x612388 + moff, 0x0000001f, link << 4 | sor); } static int -gm200_sor_dp_drv_ctl(struct nvkm_output_dp *outp, - int ln, int vs, int pe, int pc) +gm200_sor_route_get(struct nvkm_outp *outp, int *link) { - struct nvkm_device *device = outp->base.disp->engine.subdev.device; - struct nvkm_bios *bios = device->bios; - const u32 shift = gm200_sor_dp_lane_map(device, ln); - const u32 loff = gm200_sor_loff(outp); - u32 addr, data[4]; - u8 ver, hdr, cnt, len; - struct nvbios_dpout info; - struct nvbios_dpcfg ocfg; + struct nvkm_device *device = outp->disp->engine.subdev.device; + const int sublinks = outp->info.sorconf.link; + int lnk[2], sor[2], m, s; - addr = nvbios_dpout_match(bios, outp->base.info.hasht, - outp->base.info.hashm, - &ver, &hdr, &cnt, &len, &info); - if (!addr) - return -ENODEV; + for (*link = 0, m = __ffs(outp->info.or) * 2, s = 0; s < 2; m++, s++) { + if (sublinks & BIT(s)) { + u32 data = nvkm_rd32(device, 0x612308 + (m * 0x80)); + lnk[s] = (data & 0x00000010) >> 4; + sor[s] = (data & 0x0000000f); + if (!sor[s]) + return -1; + *link |= lnk[s]; + } + } - addr = nvbios_dpcfg_match(bios, addr, pc, vs, pe, - &ver, &hdr, &cnt, &len, &ocfg); - if (!addr) - return -EINVAL; - ocfg.tx_pu &= 0x0f; + if (sublinks == 3) { + if (sor[0] != sor[1] || WARN_ON(lnk[0] || !lnk[1])) + return -1; + } - data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift); - data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift); - data[2] = nvkm_rd32(device, 0x61c130 + loff); - if ((data[2] & 0x00000f00) < (ocfg.tx_pu << 8) || ln == 0) - data[2] = (data[2] & ~0x00000f00) | (ocfg.tx_pu << 8); - nvkm_wr32(device, 0x61c118 + loff, data[0] | (ocfg.dc << shift)); - nvkm_wr32(device, 0x61c120 + loff, data[1] | (ocfg.pe << shift)); - nvkm_wr32(device, 0x61c130 + loff, data[2]); - data[3] = nvkm_rd32(device, 0x61c13c + loff) & ~(0x000000ff << shift); - nvkm_wr32(device, 0x61c13c + loff, data[3] | (ocfg.pc << shift)); - return 0; + return ((sublinks & 1) ? sor[0] : sor[1]) - 1; } -static const struct nvkm_output_dp_func -gm200_sor_dp_func = { - .pattern = gm107_sor_dp_pattern, - .lnk_pwr = gm200_sor_dp_lnk_pwr, - .lnk_ctl = gf119_sor_dp_lnk_ctl, - .drv_ctl = gm200_sor_dp_drv_ctl, - .vcpi = gf119_sor_dp_vcpi, +static const struct nvkm_ior_func +gm200_sor = { + .route = { + .get = gm200_sor_route_get, + .set = gm200_sor_route_set, + }, + .state = gf119_sor_state, + .power = nv50_sor_power, + .clock = gf119_sor_clock, + .hdmi = { + .ctrl = gk104_hdmi_ctrl, + }, + .dp = { + .lanes = { 0, 1, 2, 3 }, + .links = gf119_sor_dp_links, + .power = g94_sor_dp_power, + .pattern = gm107_sor_dp_pattern, + .drive = gm200_sor_dp_drive, + .vcpi = gf119_sor_dp_vcpi, + .audio = gf119_sor_dp_audio, + .audio_sym = gf119_sor_dp_audio_sym, + .watermark = gf119_sor_dp_watermark, + }, + .hda = { + .hpd = gf119_hda_hpd, + .eld = gf119_hda_eld, + }, }; int -gm200_sor_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, - struct nvkm_output **poutp) +gm200_sor_new(struct nvkm_disp *disp, int id) { - return nvkm_output_dp_new_(&gm200_sor_dp_func, disp, index, dcbE, poutp); + return gf119_sor_new_(&gm200_sor, disp, id); } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgt215.c new file mode 100644 index 000000000000..da228b54b43e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgt215.c @@ -0,0 +1,69 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * 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 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 HOLDER(S) OR AUTHOR(S) 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 "ior.h" + +#include <subdev/timer.h> + +void +gt215_sor_dp_audio(struct nvkm_ior *sor, int head, bool enable) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); + const u32 data = 0x80000000 | (0x00000001 * enable); + const u32 mask = 0x8000000d; + nvkm_mask(device, 0x61c1e0 + soff, mask, data); + nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, 0x61c1e0 + soff) & 0x80000000)) + break; + ); +} + +static const struct nvkm_ior_func +gt215_sor = { + .state = g94_sor_state, + .power = nv50_sor_power, + .clock = nv50_sor_clock, + .hdmi = { + .ctrl = gt215_hdmi_ctrl, + }, + .dp = { + .lanes = { 2, 1, 0, 3 }, + .links = g94_sor_dp_links, + .power = g94_sor_dp_power, + .pattern = g94_sor_dp_pattern, + .drive = g94_sor_dp_drive, + .audio = gt215_sor_dp_audio, + .audio_sym = g94_sor_dp_audio_sym, + .activesym = g94_sor_dp_activesym, + .watermark = g94_sor_dp_watermark, + }, + .hda = { + .hpd = gt215_hda_hpd, + .eld = gt215_hda_eld, + }, +}; + +int +gt215_sor_new(struct nvkm_disp *disp, int id) +{ + return nv50_sor_new_(>215_sor, disp, id); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp77.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp77.c new file mode 100644 index 000000000000..c0179ccb956d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp77.c @@ -0,0 +1,48 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * 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 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 HOLDER(S) OR AUTHOR(S) 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 "ior.h" + +static const struct nvkm_ior_func +mcp77_sor = { + .state = g94_sor_state, + .power = nv50_sor_power, + .clock = nv50_sor_clock, + .hdmi = { + .ctrl = g84_hdmi_ctrl, + }, + .dp = { + .lanes = { 2, 1, 0, 3}, + .links = g94_sor_dp_links, + .power = g94_sor_dp_power, + .pattern = g94_sor_dp_pattern, + .drive = g94_sor_dp_drive, + .audio_sym = g94_sor_dp_audio_sym, + .activesym = g94_sor_dp_activesym, + .watermark = g94_sor_dp_watermark, + }, +}; + +int +mcp77_sor_new(struct nvkm_disp *disp, int id) +{ + return nv50_sor_new_(&mcp77_sor, disp, id); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp89.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp89.c new file mode 100644 index 000000000000..9bb01cd96697 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp89.c @@ -0,0 +1,53 @@ +/* + * Copyright 2017 Red Hat Inc. + * + * 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 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 HOLDER(S) OR AUTHOR(S) 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 "ior.h" + +static const struct nvkm_ior_func +mcp89_sor = { + .state = g94_sor_state, + .power = nv50_sor_power, + .clock = nv50_sor_clock, + .hdmi = { + .ctrl = gt215_hdmi_ctrl, + }, + .dp = { + .lanes = { 3, 2, 1, 0 }, + .links = g94_sor_dp_links, + .power = g94_sor_dp_power, + .pattern = g94_sor_dp_pattern, + .drive = g94_sor_dp_drive, + .audio = gt215_sor_dp_audio, + .audio_sym = g94_sor_dp_audio_sym, + .activesym = g94_sor_dp_activesym, + .watermark = g94_sor_dp_watermark, + }, + .hda = { + .hpd = gt215_hda_hpd, + .eld = gt215_hda_eld, + }, +}; + +int +mcp89_sor_new(struct nvkm_disp *disp, int id) +{ + return nv50_sor_new_(&mcp89_sor, disp, id); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sornv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sornv50.c index 53596bed3c36..f3ebd0c22e7d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sornv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sornv50.c @@ -21,59 +21,87 @@ * * Authors: Ben Skeggs */ -#include "nv50.h" -#include "outp.h" +#include "ior.h" -#include <core/client.h> #include <subdev/timer.h> -#include <nvif/cl5070.h> -#include <nvif/unpack.h> - -int -nv50_sor_power(NV50_DISP_MTHD_V1) +void +nv50_sor_clock(struct nvkm_ior *sor) { - struct nvkm_device *device = disp->base.engine.subdev.device; - union { - struct nv50_disp_sor_pwr_v0 v0; - } *args = data; - const u32 soff = outp->or * 0x800; - u32 stat; - int ret = -ENOSYS; - - nvif_ioctl(object, "disp sor pwr size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { - nvif_ioctl(object, "disp sor pwr vers %d state %d\n", - args->v0.version, args->v0.state); - stat = !!args->v0.state; - } else - return ret; - + struct nvkm_device *device = sor->disp->engine.subdev.device; + const int div = sor->asy.link == 3; + const u32 soff = nv50_ior_base(sor); + nvkm_mask(device, 0x614300 + soff, 0x00000707, (div << 8) | div); +} +static void +nv50_sor_power_wait(struct nvkm_device *device, u32 soff) +{ nvkm_msec(device, 2000, if (!(nvkm_rd32(device, 0x61c004 + soff) & 0x80000000)) break; ); - nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000000 | stat); - nvkm_msec(device, 2000, - if (!(nvkm_rd32(device, 0x61c004 + soff) & 0x80000000)) - break; - ); +} + +void +nv50_sor_power(struct nvkm_ior *sor, bool normal, bool pu, + bool data, bool vsync, bool hsync) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(sor); + const u32 shift = normal ? 0 : 16; + const u32 state = 0x80000000 | (0x00000001 * !!pu) << shift; + const u32 field = 0x80000000 | (0x00000001 << shift); + + nv50_sor_power_wait(device, soff); + nvkm_mask(device, 0x61c004 + soff, field, state); + nv50_sor_power_wait(device, soff); + nvkm_msec(device, 2000, if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000)) break; ); - return 0; } -static const struct nvkm_output_func -nv50_sor_output_func = { +void +nv50_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 coff = sor->id * 8 + (state == &sor->arm) * 4; + u32 ctrl = nvkm_rd32(device, 0x610b70 + coff); + + state->proto_evo = (ctrl & 0x00000f00) >> 8; + switch (state->proto_evo) { + case 0: state->proto = LVDS; state->link = 1; break; + case 1: state->proto = TMDS; state->link = 1; break; + case 2: state->proto = TMDS; state->link = 2; break; + case 5: state->proto = TMDS; state->link = 3; break; + default: + state->proto = UNKNOWN; + break; + } + + state->head = ctrl & 0x00000003; +} + +int +nv50_sor_new_(const struct nvkm_ior_func *func, struct nvkm_disp *disp, int id) +{ + struct nvkm_device *device = disp->engine.subdev.device; + if (!(nvkm_rd32(device, 0x610184) & (0x01000000 << id))) + return 0; + return nvkm_ior_new_(func, disp, SOR, id); +} + +static const struct nvkm_ior_func +nv50_sor = { + .state = nv50_sor_state, + .power = nv50_sor_power, + .clock = nv50_sor_clock, }; int -nv50_sor_output_new(struct nvkm_disp *disp, int index, - struct dcb_output *dcbE, struct nvkm_output **poutp) +nv50_sor_new(struct nvkm_disp *disp, int id) { - return nvkm_output_new_(&nv50_sor_output_func, disp, - index, dcbE, poutp); + return nv50_sor_new_(&nv50_sor, disp, id); } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c index 3a24788c3185..a7e55c422501 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c @@ -148,7 +148,7 @@ gk104_fifo_runlist_commit(struct gk104_fifo *fifo, int runl) case NVKM_MEM_TARGET_NCOH: target = 3; break; default: WARN_ON(1); - return; + goto unlock; } nvkm_wr32(device, 0x002270, (nvkm_memory_addr(mem) >> 12) | @@ -160,6 +160,7 @@ gk104_fifo_runlist_commit(struct gk104_fifo *fifo, int runl) & 0x00100000), msecs_to_jiffies(2000)) == 0) nvkm_error(subdev, "runlist %d update timeout\n", runl); +unlock: mutex_unlock(&subdev->mutex); } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/iccsense.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/iccsense.c index 3953d11844ea..23caef8df17f 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/iccsense.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/iccsense.c @@ -87,7 +87,10 @@ nvbios_iccsense_parse(struct nvkm_bios *bios, struct nvbios_iccsense *iccsense) switch(ver) { case 0x10: - rail->mode = nvbios_rd08(bios, entry + 0x1); + if ((nvbios_rd08(bios, entry + 0x1) & 0xf8) == 0xf8) + rail->mode = 1; + else + rail->mode = 0; rail->extdev_id = nvbios_rd08(bios, entry + 0x2); res_start = 0x3; break; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c index 38ed09fd3d2f..b58ee99f7bfc 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c @@ -37,7 +37,7 @@ #include <subdev/vga.h> #define bioslog(lvl, fmt, args...) do { \ - nvkm_printk(init->subdev, lvl, info, "0x%04x[%c]: "fmt, \ + nvkm_printk(init->subdev, lvl, info, "0x%08x[%c]: "fmt, \ init->offset, init_exec(init) ? \ '0' + (init->nested - 1) : ' ', ##args); \ } while(0) @@ -87,8 +87,8 @@ static inline int init_or(struct nvbios_init *init) { if (init_exec(init)) { - if (init->outp) - return ffs(init->outp->or) - 1; + if (init->or >= 0) + return init->or; error("script needs OR!!\n"); } return 0; @@ -98,20 +98,20 @@ static inline int init_link(struct nvbios_init *init) { if (init_exec(init)) { - if (init->outp) - return !(init->outp->sorconf.link & 1); + if (init->link) + return init->link == 2; error("script needs OR link\n"); } return 0; } static inline int -init_crtc(struct nvbios_init *init) +init_head(struct nvbios_init *init) { if (init_exec(init)) { - if (init->crtc >= 0) - return init->crtc; - error("script needs crtc\n"); + if (init->head >= 0) + return init->head; + error("script needs head\n"); } return 0; } @@ -119,7 +119,7 @@ init_crtc(struct nvbios_init *init) static u8 init_conn(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; struct nvbios_connE connE; u8 ver, hdr; u32 conn; @@ -141,7 +141,7 @@ init_conn(struct nvbios_init *init) static inline u32 init_nvreg(struct nvbios_init *init, u32 reg) { - struct nvkm_devinit *devinit = init->bios->subdev.device->devinit; + struct nvkm_devinit *devinit = init->subdev->device->devinit; /* C51 (at least) sometimes has the lower bits set which the VBIOS * interprets to mean that access needs to go through certain IO @@ -154,9 +154,9 @@ init_nvreg(struct nvbios_init *init, u32 reg) /* GF8+ display scripts need register addresses mangled a bit to * select a specific CRTC/OR */ - if (init->bios->subdev.device->card_type >= NV_50) { + if (init->subdev->device->card_type >= NV_50) { if (reg & 0x80000000) { - reg += init_crtc(init) * 0x800; + reg += init_head(init) * 0x800; reg &= ~0x80000000; } @@ -179,7 +179,7 @@ init_nvreg(struct nvbios_init *init, u32 reg) static u32 init_rd32(struct nvbios_init *init, u32 reg) { - struct nvkm_device *device = init->bios->subdev.device; + struct nvkm_device *device = init->subdev->device; reg = init_nvreg(init, reg); if (reg != ~0 && init_exec(init)) return nvkm_rd32(device, reg); @@ -189,7 +189,7 @@ init_rd32(struct nvbios_init *init, u32 reg) static void init_wr32(struct nvbios_init *init, u32 reg, u32 val) { - struct nvkm_device *device = init->bios->subdev.device; + struct nvkm_device *device = init->subdev->device; reg = init_nvreg(init, reg); if (reg != ~0 && init_exec(init)) nvkm_wr32(device, reg, val); @@ -198,7 +198,7 @@ init_wr32(struct nvbios_init *init, u32 reg, u32 val) static u32 init_mask(struct nvbios_init *init, u32 reg, u32 mask, u32 val) { - struct nvkm_device *device = init->bios->subdev.device; + struct nvkm_device *device = init->subdev->device; reg = init_nvreg(init, reg); if (reg != ~0 && init_exec(init)) { u32 tmp = nvkm_rd32(device, reg); @@ -212,7 +212,7 @@ static u8 init_rdport(struct nvbios_init *init, u16 port) { if (init_exec(init)) - return nvkm_rdport(init->subdev->device, init->crtc, port); + return nvkm_rdport(init->subdev->device, init->head, port); return 0x00; } @@ -220,7 +220,7 @@ static void init_wrport(struct nvbios_init *init, u16 port, u8 value) { if (init_exec(init)) - nvkm_wrport(init->subdev->device, init->crtc, port, value); + nvkm_wrport(init->subdev->device, init->head, port, value); } static u8 @@ -228,7 +228,7 @@ init_rdvgai(struct nvbios_init *init, u16 port, u8 index) { struct nvkm_subdev *subdev = init->subdev; if (init_exec(init)) { - int head = init->crtc < 0 ? 0 : init->crtc; + int head = init->head < 0 ? 0 : init->head; return nvkm_rdvgai(subdev->device, head, port, index); } return 0x00; @@ -242,25 +242,25 @@ init_wrvgai(struct nvbios_init *init, u16 port, u8 index, u8 value) /* force head 0 for updates to cr44, it only exists on first head */ if (device->card_type < NV_50) { if (port == 0x03d4 && index == 0x44) - init->crtc = 0; + init->head = 0; } if (init_exec(init)) { - int head = init->crtc < 0 ? 0 : init->crtc; + int head = init->head < 0 ? 0 : init->head; nvkm_wrvgai(device, head, port, index, value); } /* select head 1 if cr44 write selected it */ if (device->card_type < NV_50) { if (port == 0x03d4 && index == 0x44 && value == 3) - init->crtc = 1; + init->head = 1; } } static struct i2c_adapter * init_i2c(struct nvbios_init *init, int index) { - struct nvkm_i2c *i2c = init->bios->subdev.device->i2c; + struct nvkm_i2c *i2c = init->subdev->device->i2c; struct nvkm_i2c_bus *bus; if (index == 0xff) { @@ -300,7 +300,7 @@ init_wri2cr(struct nvbios_init *init, u8 index, u8 addr, u8 reg, u8 val) static struct nvkm_i2c_aux * init_aux(struct nvbios_init *init) { - struct nvkm_i2c *i2c = init->bios->subdev.device->i2c; + struct nvkm_i2c *i2c = init->subdev->device->i2c; if (!init->outp) { if (init_exec(init)) error("script needs output for aux\n"); @@ -341,7 +341,7 @@ init_wrauxr(struct nvbios_init *init, u32 addr, u8 data) static void init_prog_pll(struct nvbios_init *init, u32 id, u32 freq) { - struct nvkm_devinit *devinit = init->bios->subdev.device->devinit; + struct nvkm_devinit *devinit = init->subdev->device->devinit; if (init_exec(init)) { int ret = nvkm_devinit_pll_set(devinit, id, freq); if (ret) @@ -374,7 +374,7 @@ init_table(struct nvkm_bios *bios, u16 *len) static u16 init_table_(struct nvbios_init *init, u16 offset, const char *name) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u16 len, data = init_table(bios, &len); if (data) { if (len >= offset + 2) { @@ -406,7 +406,7 @@ init_table_(struct nvbios_init *init, u16 offset, const char *name) static u16 init_script(struct nvkm_bios *bios, int index) { - struct nvbios_init init = { .bios = bios }; + struct nvbios_init init = { .subdev = &bios->subdev }; u16 bmp_ver = bmp_version(bios), data; if (bmp_ver && bmp_ver < 0x0510) { @@ -436,7 +436,7 @@ init_unknown_script(struct nvkm_bios *bios) static u8 init_ram_restrict_group_count(struct nvbios_init *init) { - return nvbios_ramcfg_count(init->bios); + return nvbios_ramcfg_count(init->subdev->device->bios); } static u8 @@ -450,7 +450,7 @@ init_ram_restrict(struct nvbios_init *init) * Preserving the non-caching behaviour on earlier chipsets just * in case *not* re-reading the strap causes similar breakage. */ - if (!init->ramcfg || init->bios->version.major < 0x70) + if (!init->ramcfg || init->subdev->device->bios->version.major < 0x70) init->ramcfg = 0x80000000 | nvbios_ramcfg_index(init->subdev); return (init->ramcfg & 0x7fffffff); } @@ -458,7 +458,7 @@ init_ram_restrict(struct nvbios_init *init) static u8 init_xlat_(struct nvbios_init *init, u8 index, u8 offset) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u16 table = init_xlat_table(init); if (table) { u16 data = nvbios_rd16(bios, table + (index * 2)); @@ -476,7 +476,7 @@ init_xlat_(struct nvbios_init *init, u8 index, u8 offset) static bool init_condition_met(struct nvbios_init *init, u8 cond) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u16 table = init_condition_table(init); if (table) { u32 reg = nvbios_rd32(bios, table + (cond * 12) + 0); @@ -492,7 +492,7 @@ init_condition_met(struct nvbios_init *init, u8 cond) static bool init_io_condition_met(struct nvbios_init *init, u8 cond) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u16 table = init_io_condition_table(init); if (table) { u16 port = nvbios_rd16(bios, table + (cond * 5) + 0); @@ -509,7 +509,7 @@ init_io_condition_met(struct nvbios_init *init, u8 cond) static bool init_io_flag_condition_met(struct nvbios_init *init, u8 cond) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u16 table = init_io_flag_condition_table(init); if (table) { u16 port = nvbios_rd16(bios, table + (cond * 9) + 0); @@ -580,7 +580,8 @@ init_tmds_reg(struct nvbios_init *init, u8 tmds) static void init_reserved(struct nvbios_init *init) { - u8 opcode = nvbios_rd08(init->bios, init->offset); + struct nvkm_bios *bios = init->subdev->device->bios; + u8 opcode = nvbios_rd08(bios, init->offset); u8 length, i; switch (opcode) { @@ -594,7 +595,7 @@ init_reserved(struct nvbios_init *init) trace("RESERVED 0x%02x\t", opcode); for (i = 1; i < length; i++) - cont(" 0x%02x", nvbios_rd08(init->bios, init->offset + i)); + cont(" 0x%02x", nvbios_rd08(bios, init->offset + i)); cont("\n"); init->offset += length; } @@ -617,7 +618,7 @@ init_done(struct nvbios_init *init) static void init_io_restrict_prog(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u16 port = nvbios_rd16(bios, init->offset + 1); u8 index = nvbios_rd08(bios, init->offset + 3); u8 mask = nvbios_rd08(bios, init->offset + 4); @@ -654,7 +655,7 @@ init_io_restrict_prog(struct nvbios_init *init) static void init_repeat(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u8 count = nvbios_rd08(bios, init->offset + 1); u16 repeat = init->repeat; @@ -680,7 +681,7 @@ init_repeat(struct nvbios_init *init) static void init_io_restrict_pll(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u16 port = nvbios_rd16(bios, init->offset + 1); u8 index = nvbios_rd08(bios, init->offset + 3); u8 mask = nvbios_rd08(bios, init->offset + 4); @@ -736,7 +737,7 @@ init_end_repeat(struct nvbios_init *init) static void init_copy(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u32 reg = nvbios_rd32(bios, init->offset + 1); u8 shift = nvbios_rd08(bios, init->offset + 5); u8 smask = nvbios_rd08(bios, init->offset + 6); @@ -775,7 +776,7 @@ init_not(struct nvbios_init *init) static void init_io_flag_condition(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u8 cond = nvbios_rd08(bios, init->offset + 1); trace("IO_FLAG_CONDITION\t0x%02x\n", cond); @@ -792,7 +793,7 @@ init_io_flag_condition(struct nvbios_init *init) static void init_generic_condition(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; struct nvbios_dpout info; u8 cond = nvbios_rd08(bios, init->offset + 1); u8 size = nvbios_rd08(bios, init->offset + 2); @@ -841,7 +842,7 @@ init_generic_condition(struct nvbios_init *init) static void init_io_mask_or(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u8 index = nvbios_rd08(bios, init->offset + 1); u8 or = init_or(init); u8 data; @@ -860,7 +861,7 @@ init_io_mask_or(struct nvbios_init *init) static void init_io_or(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u8 index = nvbios_rd08(bios, init->offset + 1); u8 or = init_or(init); u8 data; @@ -879,7 +880,7 @@ init_io_or(struct nvbios_init *init) static void init_andn_reg(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u32 reg = nvbios_rd32(bios, init->offset + 1); u32 mask = nvbios_rd32(bios, init->offset + 5); @@ -896,7 +897,7 @@ init_andn_reg(struct nvbios_init *init) static void init_or_reg(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u32 reg = nvbios_rd32(bios, init->offset + 1); u32 mask = nvbios_rd32(bios, init->offset + 5); @@ -913,7 +914,7 @@ init_or_reg(struct nvbios_init *init) static void init_idx_addr_latched(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u32 creg = nvbios_rd32(bios, init->offset + 1); u32 dreg = nvbios_rd32(bios, init->offset + 5); u32 mask = nvbios_rd32(bios, init->offset + 9); @@ -943,7 +944,7 @@ init_idx_addr_latched(struct nvbios_init *init) static void init_io_restrict_pll2(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u16 port = nvbios_rd16(bios, init->offset + 1); u8 index = nvbios_rd08(bios, init->offset + 3); u8 mask = nvbios_rd08(bios, init->offset + 4); @@ -978,7 +979,7 @@ init_io_restrict_pll2(struct nvbios_init *init) static void init_pll2(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u32 reg = nvbios_rd32(bios, init->offset + 1); u32 freq = nvbios_rd32(bios, init->offset + 5); @@ -995,7 +996,7 @@ init_pll2(struct nvbios_init *init) static void init_i2c_byte(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u8 index = nvbios_rd08(bios, init->offset + 1); u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1; u8 count = nvbios_rd08(bios, init->offset + 3); @@ -1026,7 +1027,7 @@ init_i2c_byte(struct nvbios_init *init) static void init_zm_i2c_byte(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u8 index = nvbios_rd08(bios, init->offset + 1); u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1; u8 count = nvbios_rd08(bios, init->offset + 3); @@ -1052,7 +1053,7 @@ init_zm_i2c_byte(struct nvbios_init *init) static void init_zm_i2c(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u8 index = nvbios_rd08(bios, init->offset + 1); u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1; u8 count = nvbios_rd08(bios, init->offset + 3); @@ -1086,7 +1087,7 @@ init_zm_i2c(struct nvbios_init *init) static void init_tmds(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u8 tmds = nvbios_rd08(bios, init->offset + 1); u8 addr = nvbios_rd08(bios, init->offset + 2); u8 mask = nvbios_rd08(bios, init->offset + 3); @@ -1112,7 +1113,7 @@ init_tmds(struct nvbios_init *init) static void init_zm_tmds_group(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u8 tmds = nvbios_rd08(bios, init->offset + 1); u8 count = nvbios_rd08(bios, init->offset + 2); u32 reg = init_tmds_reg(init, tmds); @@ -1139,7 +1140,7 @@ init_zm_tmds_group(struct nvbios_init *init) static void init_cr_idx_adr_latch(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u8 addr0 = nvbios_rd08(bios, init->offset + 1); u8 addr1 = nvbios_rd08(bios, init->offset + 2); u8 base = nvbios_rd08(bios, init->offset + 3); @@ -1169,7 +1170,7 @@ init_cr_idx_adr_latch(struct nvbios_init *init) static void init_cr(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u8 addr = nvbios_rd08(bios, init->offset + 1); u8 mask = nvbios_rd08(bios, init->offset + 2); u8 data = nvbios_rd08(bios, init->offset + 3); @@ -1189,7 +1190,7 @@ init_cr(struct nvbios_init *init) static void init_zm_cr(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u8 addr = nvbios_rd08(bios, init->offset + 1); u8 data = nvbios_rd08(bios, init->offset + 2); @@ -1206,7 +1207,7 @@ init_zm_cr(struct nvbios_init *init) static void init_zm_cr_group(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u8 count = nvbios_rd08(bios, init->offset + 1); trace("ZM_CR_GROUP\n"); @@ -1230,7 +1231,7 @@ init_zm_cr_group(struct nvbios_init *init) static void init_condition_time(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u8 cond = nvbios_rd08(bios, init->offset + 1); u8 retry = nvbios_rd08(bios, init->offset + 2); u8 wait = min((u16)retry * 50, 100); @@ -1257,7 +1258,7 @@ init_condition_time(struct nvbios_init *init) static void init_ltime(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u16 msec = nvbios_rd16(bios, init->offset + 1); trace("LTIME\t0x%04x\n", msec); @@ -1274,7 +1275,7 @@ init_ltime(struct nvbios_init *init) static void init_zm_reg_sequence(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u32 base = nvbios_rd32(bios, init->offset + 1); u8 count = nvbios_rd08(bios, init->offset + 5); @@ -1299,7 +1300,7 @@ init_zm_reg_sequence(struct nvbios_init *init) static void init_pll_indirect(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u32 reg = nvbios_rd32(bios, init->offset + 1); u16 addr = nvbios_rd16(bios, init->offset + 5); u32 freq = (u32)nvbios_rd16(bios, addr) * 1000; @@ -1318,7 +1319,7 @@ init_pll_indirect(struct nvbios_init *init) static void init_zm_reg_indirect(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u32 reg = nvbios_rd32(bios, init->offset + 1); u16 addr = nvbios_rd16(bios, init->offset + 5); u32 data = nvbios_rd32(bios, addr); @@ -1337,7 +1338,7 @@ init_zm_reg_indirect(struct nvbios_init *init) static void init_sub_direct(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u16 addr = nvbios_rd16(bios, init->offset + 1); u16 save; @@ -1363,7 +1364,7 @@ init_sub_direct(struct nvbios_init *init) static void init_jump(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u16 offset = nvbios_rd16(bios, init->offset + 1); trace("JUMP\t0x%04x\n", offset); @@ -1381,7 +1382,7 @@ init_jump(struct nvbios_init *init) static void init_i2c_if(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u8 index = nvbios_rd08(bios, init->offset + 1); u8 addr = nvbios_rd08(bios, init->offset + 2); u8 reg = nvbios_rd08(bios, init->offset + 3); @@ -1408,7 +1409,7 @@ init_i2c_if(struct nvbios_init *init) static void init_copy_nv_reg(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u32 sreg = nvbios_rd32(bios, init->offset + 1); u8 shift = nvbios_rd08(bios, init->offset + 5); u32 smask = nvbios_rd32(bios, init->offset + 6); @@ -1434,7 +1435,7 @@ init_copy_nv_reg(struct nvbios_init *init) static void init_zm_index_io(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u16 port = nvbios_rd16(bios, init->offset + 1); u8 index = nvbios_rd08(bios, init->offset + 3); u8 data = nvbios_rd08(bios, init->offset + 4); @@ -1452,7 +1453,7 @@ init_zm_index_io(struct nvbios_init *init) static void init_compute_mem(struct nvbios_init *init) { - struct nvkm_devinit *devinit = init->bios->subdev.device->devinit; + struct nvkm_devinit *devinit = init->subdev->device->devinit; trace("COMPUTE_MEM\n"); init->offset += 1; @@ -1470,7 +1471,7 @@ init_compute_mem(struct nvbios_init *init) static void init_reset(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u32 reg = nvbios_rd32(bios, init->offset + 1); u32 data1 = nvbios_rd32(bios, init->offset + 5); u32 data2 = nvbios_rd32(bios, init->offset + 9); @@ -1497,7 +1498,7 @@ init_reset(struct nvbios_init *init) static u16 init_configure_mem_clk(struct nvbios_init *init) { - u16 mdata = bmp_mem_init_table(init->bios); + u16 mdata = bmp_mem_init_table(init->subdev->device->bios); if (mdata) mdata += (init_rdvgai(init, 0x03d4, 0x3c) >> 4) * 66; return mdata; @@ -1506,7 +1507,7 @@ init_configure_mem_clk(struct nvbios_init *init) static void init_configure_mem(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u16 mdata, sdata; u32 addr, data; @@ -1556,7 +1557,7 @@ init_configure_mem(struct nvbios_init *init) static void init_configure_clk(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u16 mdata, clock; trace("CONFIGURE_CLK\n"); @@ -1590,7 +1591,7 @@ init_configure_clk(struct nvbios_init *init) static void init_configure_preinit(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u32 strap; trace("CONFIGURE_PREINIT\n"); @@ -1616,7 +1617,7 @@ init_configure_preinit(struct nvbios_init *init) static void init_io(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u16 port = nvbios_rd16(bios, init->offset + 1); u8 mask = nvbios_rd16(bios, init->offset + 3); u8 data = nvbios_rd16(bios, init->offset + 4); @@ -1656,7 +1657,7 @@ init_io(struct nvbios_init *init) static void init_sub(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u8 index = nvbios_rd08(bios, init->offset + 1); u16 addr, save; @@ -1683,7 +1684,7 @@ init_sub(struct nvbios_init *init) static void init_ram_condition(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u8 mask = nvbios_rd08(bios, init->offset + 1); u8 value = nvbios_rd08(bios, init->offset + 2); @@ -1702,7 +1703,7 @@ init_ram_condition(struct nvbios_init *init) static void init_nv_reg(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u32 reg = nvbios_rd32(bios, init->offset + 1); u32 mask = nvbios_rd32(bios, init->offset + 5); u32 data = nvbios_rd32(bios, init->offset + 9); @@ -1720,7 +1721,7 @@ init_nv_reg(struct nvbios_init *init) static void init_macro(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u8 macro = nvbios_rd08(bios, init->offset + 1); u16 table; @@ -1756,7 +1757,7 @@ init_resume(struct nvbios_init *init) static void init_strap_condition(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u32 mask = nvbios_rd32(bios, init->offset + 1); u32 value = nvbios_rd32(bios, init->offset + 5); @@ -1774,7 +1775,7 @@ init_strap_condition(struct nvbios_init *init) static void init_time(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u16 usec = nvbios_rd16(bios, init->offset + 1); trace("TIME\t0x%04x\n", usec); @@ -1795,7 +1796,7 @@ init_time(struct nvbios_init *init) static void init_condition(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u8 cond = nvbios_rd08(bios, init->offset + 1); trace("CONDITION\t0x%02x\n", cond); @@ -1812,7 +1813,7 @@ init_condition(struct nvbios_init *init) static void init_io_condition(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u8 cond = nvbios_rd08(bios, init->offset + 1); trace("IO_CONDITION\t0x%02x\n", cond); @@ -1829,7 +1830,7 @@ init_io_condition(struct nvbios_init *init) static void init_zm_reg16(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u32 addr = nvbios_rd32(bios, init->offset + 1); u16 data = nvbios_rd16(bios, init->offset + 5); @@ -1846,7 +1847,7 @@ init_zm_reg16(struct nvbios_init *init) static void init_index_io(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u16 port = nvbios_rd16(bios, init->offset + 1); u8 index = nvbios_rd16(bios, init->offset + 3); u8 mask = nvbios_rd08(bios, init->offset + 4); @@ -1868,7 +1869,7 @@ init_index_io(struct nvbios_init *init) static void init_pll(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u32 reg = nvbios_rd32(bios, init->offset + 1); u32 freq = nvbios_rd16(bios, init->offset + 5) * 10; @@ -1885,7 +1886,7 @@ init_pll(struct nvbios_init *init) static void init_zm_reg(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u32 addr = nvbios_rd32(bios, init->offset + 1); u32 data = nvbios_rd32(bios, init->offset + 5); @@ -1905,7 +1906,7 @@ init_zm_reg(struct nvbios_init *init) static void init_ram_restrict_pll(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u8 type = nvbios_rd08(bios, init->offset + 1); u8 count = init_ram_restrict_group_count(init); u8 strap = init_ram_restrict(init); @@ -1935,7 +1936,7 @@ init_ram_restrict_pll(struct nvbios_init *init) static void init_gpio(struct nvbios_init *init) { - struct nvkm_gpio *gpio = init->bios->subdev.device->gpio; + struct nvkm_gpio *gpio = init->subdev->device->gpio; trace("GPIO\n"); init->offset += 1; @@ -1951,7 +1952,7 @@ init_gpio(struct nvbios_init *init) static void init_ram_restrict_zm_reg_group(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u32 addr = nvbios_rd32(bios, init->offset + 1); u8 incr = nvbios_rd08(bios, init->offset + 5); u8 num = nvbios_rd08(bios, init->offset + 6); @@ -1989,7 +1990,7 @@ init_ram_restrict_zm_reg_group(struct nvbios_init *init) static void init_copy_zm_reg(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u32 sreg = nvbios_rd32(bios, init->offset + 1); u32 dreg = nvbios_rd32(bios, init->offset + 5); @@ -2006,7 +2007,7 @@ init_copy_zm_reg(struct nvbios_init *init) static void init_zm_reg_group(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u32 addr = nvbios_rd32(bios, init->offset + 1); u8 count = nvbios_rd08(bios, init->offset + 5); @@ -2028,7 +2029,7 @@ init_zm_reg_group(struct nvbios_init *init) static void init_xlat(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u32 saddr = nvbios_rd32(bios, init->offset + 1); u8 sshift = nvbios_rd08(bios, init->offset + 5); u8 smask = nvbios_rd08(bios, init->offset + 6); @@ -2056,7 +2057,7 @@ init_xlat(struct nvbios_init *init) static void init_zm_mask_add(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u32 addr = nvbios_rd32(bios, init->offset + 1); u32 mask = nvbios_rd32(bios, init->offset + 5); u32 add = nvbios_rd32(bios, init->offset + 9); @@ -2077,7 +2078,7 @@ init_zm_mask_add(struct nvbios_init *init) static void init_auxch(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u32 addr = nvbios_rd32(bios, init->offset + 1); u8 count = nvbios_rd08(bios, init->offset + 5); @@ -2101,7 +2102,7 @@ init_auxch(struct nvbios_init *init) static void init_zm_auxch(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u32 addr = nvbios_rd32(bios, init->offset + 1); u8 count = nvbios_rd08(bios, init->offset + 5); @@ -2123,7 +2124,7 @@ init_zm_auxch(struct nvbios_init *init) static void init_i2c_long_if(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; u8 index = nvbios_rd08(bios, init->offset + 1); u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1; u8 reglo = nvbios_rd08(bios, init->offset + 3); @@ -2162,7 +2163,7 @@ init_i2c_long_if(struct nvbios_init *init) static void init_gpio_ne(struct nvbios_init *init) { - struct nvkm_bios *bios = init->bios; + struct nvkm_bios *bios = init->subdev->device->bios; struct nvkm_gpio *gpio = bios->subdev.device->gpio; struct dcb_gpio_func func; u8 count = nvbios_rd08(bios, init->offset + 1); @@ -2275,9 +2276,11 @@ static struct nvbios_init_opcode { int nvbios_exec(struct nvbios_init *init) { + struct nvkm_bios *bios = init->subdev->device->bios; + init->nested++; while (init->offset) { - u8 opcode = nvbios_rd08(init->bios, init->offset); + u8 opcode = nvbios_rd08(bios, init->offset); if (opcode >= init_opcode_nr || !init_opcode[opcode].exec) { error("unknown opcode 0x%02x\n", opcode); return -EINVAL; @@ -2290,7 +2293,7 @@ nvbios_exec(struct nvbios_init *init) } int -nvbios_init(struct nvkm_subdev *subdev, bool execute) +nvbios_post(struct nvkm_subdev *subdev, bool execute) { struct nvkm_bios *bios = subdev->device->bios; int ret = 0; @@ -2300,32 +2303,18 @@ nvbios_init(struct nvkm_subdev *subdev, bool execute) if (execute) nvkm_debug(subdev, "running init tables\n"); while (!ret && (data = (init_script(bios, ++i)))) { - struct nvbios_init init = { - .subdev = subdev, - .bios = bios, - .offset = data, - .outp = NULL, - .crtc = -1, - .execute = execute ? 1 : 0, - }; - - ret = nvbios_exec(&init); + ret = nvbios_init(subdev, data, + init.execute = execute ? 1 : 0; + ); } /* the vbios parser will run this right after the normal init * tables, whereas the binary driver appears to run it later. */ if (!ret && (data = init_unknown_script(bios))) { - struct nvbios_init init = { - .subdev = subdev, - .bios = bios, - .offset = data, - .outp = NULL, - .crtc = -1, - .execute = execute ? 1 : 0, - }; - - ret = nvbios_exec(&init); + ret = nvbios_init(subdev, data, + init.execute = execute ? 1 : 0; + ); } return ret; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c index a7797a9e9cbc..7143ea4611aa 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c @@ -93,9 +93,9 @@ nvbios_volt_parse(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, info->step = nvbios_rd16(bios, volt + 0x08); info->vidmask = nvbios_rd08(bios, volt + 0x0b); info->ranged = true; /* XXX: find the flag byte */ - /*XXX*/ - info->min = 0; - info->max = info->base; + info->min = min(info->base, + info->base + info->step * info->vidmask); + info->max = nvbios_rd32(bios, volt + 0x0e); break; case 0x50: info->min = nvbios_rd32(bios, volt + 0x0a); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c index c8d455346fcd..158977f8a6e6 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c @@ -393,7 +393,7 @@ nv04_devinit_pll_set(struct nvkm_devinit *devinit, u32 type, u32 freq) int nv04_devinit_post(struct nvkm_devinit *init, bool execute) { - return nvbios_init(&init->subdev, execute); + return nvbios_post(&init->subdev, execute); } void diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c index 59362f8dee22..d7947c4391dc 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c @@ -137,16 +137,11 @@ nv50_devinit_init(struct nvkm_devinit *base) while (init->base.post && dcb_outp_parse(bios, i, &ver, &hdr, &outp)) { if (nvbios_outp_match(bios, outp.hasht, outp.hashm, &ver, &hdr, &cnt, &len, &info)) { - struct nvbios_init exec = { - .subdev = subdev, - .bios = bios, - .offset = info.script[0], - .outp = &outp, - .crtc = -1, - .execute = 1, - }; - - nvbios_exec(&exec); + nvbios_init(subdev, info.script[0], + init.outp = &outp; + init.or = ffs(outp.or) - 1; + init.link = outp.sorconf.link == 2; + ); } i++; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c index f6c00791722c..75814f15eb53 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c @@ -1424,12 +1424,7 @@ gk104_ram_init(struct nvkm_ram *ram) for (i = 0; i < cnt; i++, data += 4) { if (i != save >> 4) { nvkm_mask(device, 0x10f65c, 0x000000f0, i << 4); - nvbios_exec(&(struct nvbios_init) { - .subdev = subdev, - .bios = bios, - .offset = nvbios_rd32(bios, data), - .execute = 1, - }); + nvbios_init(subdev, nvbios_rd32(bios, data)); } } nvkm_mask(device, 0x10f65c, 0x000000f0, save); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgp100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgp100.c index cac70047ad5a..df8a87333b67 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgp100.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgp100.c @@ -59,12 +59,7 @@ gp100_ram_init(struct nvkm_ram *ram) for (i = 0; i < cnt; i++, data += 4) { if (i != save >> 4) { nvkm_mask(device, 0x9a065c, 0x000000f0, i << 4); - nvbios_exec(&(struct nvbios_init) { - .subdev = subdev, - .bios = bios, - .offset = nvbios_rd32(bios, data), - .execute = 1, - }); + nvbios_init(subdev, nvbios_rd32(bios, data)); } } nvkm_mask(device, 0x9a065c, 0x000000f0, save); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c index 56f8cffc2560..70c63535d56b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c @@ -150,16 +150,8 @@ nv40_ram_prog(struct nvkm_ram *base) udelay(100); /* execute memory reset script from vbios */ - if (!bit_entry(bios, 'M', &M)) { - struct nvbios_init init = { - .subdev = subdev, - .bios = bios, - .offset = nvbios_rd16(bios, M.offset + 0x00), - .execute = 1, - }; - - nvbios_exec(&init); - } + if (!bit_entry(bios, 'M', &M)) + nvbios_init(subdev, nvbios_rd16(bios, M.offset + 0x00)); /* make sure we're in vblank (hopefully the same one as before), and * then re-enable crtc memory access diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c index 9ca0db796cbe..978aae3c1001 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c @@ -158,7 +158,7 @@ static void gk20a_pmu_fini(struct nvkm_pmu *pmu) { struct gk20a_pmu *gpmu = gk20a_pmu(pmu); - nvkm_timer_alarm_cancel(pmu->subdev.device->timer, &gpmu->alarm); + nvkm_timer_alarm(pmu->subdev.device->timer, 0, &gpmu->alarm); nvkm_falcon_put(pmu->falcon, &pmu->subdev); } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode_gr.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode_gr.c index d1cf02d22db1..1b0c793c0192 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode_gr.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode_gr.c @@ -116,6 +116,7 @@ ls_ucode_img_load_gr(const struct nvkm_subdev *subdev, struct ls_ucode_img *img, ret = nvkm_firmware_get(subdev->device, f, &sig); if (ret) goto free_data; + img->sig = kmemdup(sig->data, sig->size, GFP_KERNEL); if (!img->sig) { ret = -ENOMEM; @@ -126,8 +127,9 @@ ls_ucode_img_load_gr(const struct nvkm_subdev *subdev, struct ls_ucode_img *img, img->ucode_data = ls_ucode_img_build(bl, code, data, &img->ucode_desc); if (IS_ERR(img->ucode_data)) { + kfree(img->sig); ret = PTR_ERR(img->ucode_data); - goto free_data; + goto free_sig; } img->ucode_size = img->ucode_desc.image_size; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c index be691a7b972f..952a7cb0a59a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c @@ -116,7 +116,7 @@ nvkm_therm_update(struct nvkm_therm *therm, int mode) switch (mode) { case NVKM_THERM_CTRL_MANUAL: - nvkm_timer_alarm_cancel(tmr, &therm->alarm); + nvkm_timer_alarm(tmr, 0, &therm->alarm); duty = nvkm_therm_fan_get(therm); if (duty < 0) duty = 100; @@ -142,7 +142,7 @@ nvkm_therm_update(struct nvkm_therm *therm, int mode) break; case NVKM_THERM_CTRL_NONE: default: - nvkm_timer_alarm_cancel(tmr, &therm->alarm); + nvkm_timer_alarm(tmr, 0, &therm->alarm); poll = false; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c index e2feccec25f5..f8fa43c8a7d2 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c @@ -215,7 +215,7 @@ nvkm_therm_fan_fini(struct nvkm_therm *therm, bool suspend) { struct nvkm_timer *tmr = therm->subdev.device->timer; if (suspend) - nvkm_timer_alarm_cancel(tmr, &therm->fan->alarm); + nvkm_timer_alarm(tmr, 0, &therm->fan->alarm); return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c index 9a79e91fdfdc..e93b2410c38b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c @@ -220,7 +220,7 @@ nvkm_therm_sensor_fini(struct nvkm_therm *therm, bool suspend) { struct nvkm_timer *tmr = therm->subdev.device->timer; if (suspend) - nvkm_timer_alarm_cancel(tmr, &therm->sensor.therm_poll_alarm); + nvkm_timer_alarm(tmr, 0, &therm->sensor.therm_poll_alarm); return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c index f2a86eae0a0d..36de23d12ae4 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c @@ -50,7 +50,8 @@ nvkm_timer_alarm_trigger(struct nvkm_timer *tmr) /* Move to completed list. We'll drop the lock before * executing the callback so it can reschedule itself. */ - list_move_tail(&alarm->head, &exec); + list_del_init(&alarm->head); + list_add(&alarm->exec, &exec); } /* Shut down interrupt if no more pending alarms. */ @@ -59,8 +60,8 @@ nvkm_timer_alarm_trigger(struct nvkm_timer *tmr) spin_unlock_irqrestore(&tmr->lock, flags); /* Execute completed callbacks. */ - list_for_each_entry_safe(alarm, atemp, &exec, head) { - list_del_init(&alarm->head); + list_for_each_entry_safe(alarm, atemp, &exec, exec) { + list_del(&alarm->exec); alarm->func(alarm); } } @@ -104,15 +105,6 @@ nvkm_timer_alarm(struct nvkm_timer *tmr, u32 nsec, struct nvkm_alarm *alarm) spin_unlock_irqrestore(&tmr->lock, flags); } -void -nvkm_timer_alarm_cancel(struct nvkm_timer *tmr, struct nvkm_alarm *alarm) -{ - unsigned long flags; - spin_lock_irqsave(&tmr->lock, flags); - list_del_init(&alarm->head); - spin_unlock_irqrestore(&tmr->lock, flags); -} - static void nvkm_timer_intr(struct nvkm_subdev *subdev) { |