From dc854914999d5d52ac1b31740cb0ea8d89d0372e Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Thu, 6 Sep 2018 17:43:21 -0400 Subject: [PATCH 01/14] drm/nouveau: Check backlight IDs are >= 0, not > 0 Remember, ida IDs start at 0, not 1! Signed-off-by: Lyude Paul Reviewed-by: Karol Herbst Cc: stable@vger.kernel.org Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_backlight.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c index 408b955e5c39..6dd72bc32897 100644 --- a/drivers/gpu/drm/nouveau/nouveau_backlight.c +++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c @@ -116,7 +116,7 @@ nv40_backlight_init(struct drm_connector *connector) &nv40_bl_ops, &props); if (IS_ERR(bd)) { - if (bl_connector.id > 0) + if (bl_connector.id >= 0) ida_simple_remove(&bl_ida, bl_connector.id); return PTR_ERR(bd); } @@ -249,7 +249,7 @@ nv50_backlight_init(struct drm_connector *connector) nv_encoder, ops, &props); if (IS_ERR(bd)) { - if (bl_connector.id > 0) + if (bl_connector.id >= 0) ida_simple_remove(&bl_ida, bl_connector.id); return PTR_ERR(bd); } From 4c497075042897ad287b1119c3c394812b292238 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Thu, 6 Sep 2018 17:43:22 -0400 Subject: [PATCH 02/14] drm/nouveau: Add NV_PRINTK_ONCE and variants Since we're about to use this in nouveau_backlight.c. Same thing as DRM_WARN_ONCE, DRM_INFO_ONCE, etc... Signed-off-by: Lyude Paul Cc: Karol Herbst Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 6e1acaec3400..d9d687916553 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -244,10 +244,12 @@ void nouveau_drm_device_remove(struct drm_device *dev); struct nouveau_cli *_cli = (c); \ dev_##l(_cli->drm->dev->dev, "%s: "f, _cli->name, ##a); \ } while(0) + #define NV_FATAL(drm,f,a...) NV_PRINTK(crit, &(drm)->client, f, ##a) #define NV_ERROR(drm,f,a...) NV_PRINTK(err, &(drm)->client, f, ##a) #define NV_WARN(drm,f,a...) NV_PRINTK(warn, &(drm)->client, f, ##a) #define NV_INFO(drm,f,a...) NV_PRINTK(info, &(drm)->client, f, ##a) + #define NV_DEBUG(drm,f,a...) do { \ if (unlikely(drm_debug & DRM_UT_DRIVER)) \ NV_PRINTK(info, &(drm)->client, f, ##a); \ @@ -257,6 +259,12 @@ void nouveau_drm_device_remove(struct drm_device *dev); NV_PRINTK(info, &(drm)->client, f, ##a); \ } while(0) +#define NV_PRINTK_ONCE(l,c,f,a...) NV_PRINTK(l##_once,c,f, ##a) + +#define NV_ERROR_ONCE(drm,f,a...) NV_PRINTK_ONCE(err, &(drm)->client, f, ##a) +#define NV_WARN_ONCE(drm,f,a...) NV_PRINTK_ONCE(warn, &(drm)->client, f, ##a) +#define NV_INFO_ONCE(drm,f,a...) NV_PRINTK_ONCE(info, &(drm)->client, f, ##a) + extern int nouveau_modeset; #endif From 6d757753cef8d523342fe544299068d30b5be490 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Thu, 6 Sep 2018 17:43:23 -0400 Subject: [PATCH 03/14] drm/nouveau: Move backlight device into nouveau_connector Currently module unloading is broken in nouveau due to a rather annoying race condition resulting from nouveau_backlight.c having gone a bit stale over time: [ 1960.791143] ================================================================== [ 1960.791394] BUG: KASAN: use-after-free in nouveau_backlight_exit+0x112/0x150 [nouveau] [ 1960.791460] Read of size 4 at addr ffff88075accf350 by task zsh/11185 [ 1960.791521] [ 1960.791545] CPU: 7 PID: 11185 Comm: zsh Kdump: loaded Tainted: G O 4.18.0Lyude-Test+ #4 [ 1960.791580] Hardware name: LENOVO 20EQS64N0B/20EQS64N0B, BIOS N1EET79W (1.52 ) 07/13/2018 [ 1960.791628] Call Trace: [ 1960.791680] dump_stack+0xa4/0xfd [ 1960.791721] print_address_description+0x71/0x239 [ 1960.791833] ? nouveau_backlight_exit+0x112/0x150 [nouveau] [ 1960.791877] kasan_report.cold.6+0x242/0x2fe [ 1960.791919] __asan_report_load4_noabort+0x19/0x20 [ 1960.792012] nouveau_backlight_exit+0x112/0x150 [nouveau] [ 1960.792081] nouveau_display_destroy+0x76/0x150 [nouveau] [ 1960.792150] nouveau_drm_device_fini+0xb7/0x190 [nouveau] [ 1960.792265] nouveau_drm_device_remove+0x14b/0x1d0 [nouveau] [ 1960.792347] ? nouveau_cli_work_queue+0x2e0/0x2e0 [nouveau] [ 1960.792378] ? trace_hardirqs_on_caller+0x38b/0x570 [ 1960.792406] ? trace_hardirqs_on+0xd/0x10 [ 1960.792472] nouveau_drm_remove+0x37/0x50 [nouveau] [ 1960.792502] pci_device_remove+0x112/0x2d0 [ 1960.792530] ? pcibios_free_irq+0x10/0x10 [ 1960.792558] ? kasan_check_write+0x14/0x20 [ 1960.792587] device_release_driver_internal+0x35c/0x650 [ 1960.792617] device_release_driver+0x12/0x20 [ 1960.792643] pci_stop_bus_device+0x172/0x1e0 [ 1960.792671] pci_stop_and_remove_bus_device_locked+0x1a/0x30 [ 1960.792715] remove_store+0xcb/0xe0 [ 1960.792753] ? sriov_numvfs_store+0x2e0/0x2e0 [ 1960.792779] ? __lock_is_held+0xb5/0x140 [ 1960.792808] ? component_add+0x530/0x530 [ 1960.792834] dev_attr_store+0x3f/0x70 [ 1960.792859] ? sysfs_file_ops+0x11d/0x170 [ 1960.792885] sysfs_kf_write+0x104/0x150 [ 1960.792915] ? sysfs_file_ops+0x170/0x170 [ 1960.792940] kernfs_fop_write+0x24f/0x400 [ 1960.792978] ? __lock_acquire+0x6ea/0x47f0 [ 1960.793021] __vfs_write+0xeb/0x760 [ 1960.793048] ? kernel_read+0x130/0x130 [ 1960.793076] ? __lock_is_held+0xb5/0x140 [ 1960.793107] ? rcu_read_lock_sched_held+0xdd/0x110 [ 1960.793135] ? rcu_sync_lockdep_assert+0x78/0xb0 [ 1960.793162] ? __sb_start_write+0x183/0x220 [ 1960.793189] vfs_write+0x14d/0x4a0 [ 1960.793229] ksys_write+0xd2/0x1b0 [ 1960.793255] ? __ia32_sys_read+0xb0/0xb0 [ 1960.793298] ? fput+0x1d/0x120 [ 1960.793324] ? filp_close+0xf3/0x130 [ 1960.793349] ? entry_SYSCALL_64_after_hwframe+0x59/0xbe [ 1960.793380] __x64_sys_write+0x73/0xb0 [ 1960.793407] do_syscall_64+0xaa/0x400 [ 1960.793433] entry_SYSCALL_64_after_hwframe+0x49/0xbe [ 1960.793460] RIP: 0033:0x7f59df433164 [ 1960.793486] Code: 89 02 48 c7 c0 ff ff ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 66 90 48 8d 05 81 38 2d 00 8b 00 85 c0 75 13 b8 01 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 54 c3 0f 1f 00 41 54 49 89 d4 55 48 89 f5 53 [ 1960.793541] RSP: 002b:00007ffd70ee2fb8 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 [ 1960.793576] RAX: ffffffffffffffda RBX: 0000000000000002 RCX: 00007f59df433164 [ 1960.793620] RDX: 0000000000000002 RSI: 00005578088640c0 RDI: 0000000000000001 [ 1960.793665] RBP: 00005578088640c0 R08: 00007f59df7038c0 R09: 00007f59e0995b80 [ 1960.793696] R10: 000000000000000a R11: 0000000000000246 R12: 00007f59df702760 [ 1960.793730] R13: 0000000000000002 R14: 00007f59df6fd760 R15: 0000000000000002 [ 1960.793768] [ 1960.793790] Allocated by task 11167: [ 1960.793816] save_stack+0x43/0xd0 [ 1960.793841] kasan_kmalloc+0xc4/0xe0 [ 1960.793880] kasan_slab_alloc+0x11/0x20 [ 1960.793905] kmem_cache_alloc+0xd7/0x270 [ 1960.793944] getname_flags+0xbd/0x520 [ 1960.793969] user_path_at_empty+0x23/0x50 [ 1960.793994] do_faccessat+0x1fc/0x5d0 [ 1960.794018] __x64_sys_access+0x59/0x80 [ 1960.794043] do_syscall_64+0xaa/0x400 [ 1960.794067] entry_SYSCALL_64_after_hwframe+0x49/0xbe [ 1960.794093] [ 1960.794127] Freed by task 11167: [ 1960.794152] save_stack+0x43/0xd0 [ 1960.794190] __kasan_slab_free+0x139/0x190 [ 1960.794215] kasan_slab_free+0xe/0x10 [ 1960.794239] kmem_cache_free+0xcb/0x2c0 [ 1960.794264] putname+0xad/0xe0 [ 1960.794287] filename_lookup.part.59+0x1f1/0x360 [ 1960.794313] user_path_at_empty+0x3e/0x50 [ 1960.794338] do_faccessat+0x1fc/0x5d0 [ 1960.794362] __x64_sys_access+0x59/0x80 [ 1960.794393] do_syscall_64+0xaa/0x400 [ 1960.794421] entry_SYSCALL_64_after_hwframe+0x49/0xbe [ 1960.794461] [ 1960.794483] The buggy address belongs to the object at ffff88075acceac0 [ 1960.794483] which belongs to the cache names_cache of size 4096 [ 1960.794540] The buggy address is located 2192 bytes inside of [ 1960.794540] 4096-byte region [ffff88075acceac0, ffff88075accfac0) [ 1960.794581] The buggy address belongs to the page: [ 1960.794609] page:ffffea001d6b3200 count:1 mapcount:0 mapping:ffff880778e4b1c0 index:0x0 compound_mapcount: 0 [ 1960.794651] flags: 0x8000000000008100(slab|head) [ 1960.794679] raw: 8000000000008100 ffffea001d39e808 ffffea001d39ea08 ffff880778e4b1c0 [ 1960.794739] raw: 0000000000000000 0000000000070007 00000001ffffffff 0000000000000000 [ 1960.794785] page dumped because: kasan: bad access detected [ 1960.794813] [ 1960.794834] Memory state around the buggy address: [ 1960.794861] ffff88075accf200: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 1960.794894] ffff88075accf280: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 1960.794925] >ffff88075accf300: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 1960.794956] ^ [ 1960.794985] ffff88075accf380: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 1960.795017] ffff88075accf400: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 1960.795061] ================================================================== [ 1960.795106] Disabling lock debugging due to kernel taint [ 1960.795131] ------------[ cut here ]------------ [ 1960.795148] ida_remove called for id=1802201963 which is not allocated. [ 1960.795193] WARNING: CPU: 7 PID: 11185 at lib/idr.c:521 ida_remove+0x184/0x210 [ 1960.795213] Modules linked in: nouveau(O) mxm_wmi ttm i2c_algo_bit drm_kms_helper syscopyarea sysfillrect sysimgblt fb_sys_fops drm joydev vfat fat intel_rapl x86_pkg_temp_thermal coretemp crc32_pclmul iTCO_wdt psmouse wmi_bmof mei_me tpm_tis mei tpm_tis_core tpm i2c_i801 thinkpad_acpi pcc_cpufreq crc32c_intel serio_raw xhci_pci xhci_hcd wmi video i2c_dev i2c_core [ 1960.795305] CPU: 7 PID: 11185 Comm: zsh Kdump: loaded Tainted: G B O 4.18.0Lyude-Test+ #4 [ 1960.795330] Hardware name: LENOVO 20EQS64N0B/20EQS64N0B, BIOS N1EET79W (1.52 ) 07/13/2018 [ 1960.795352] RIP: 0010:ida_remove+0x184/0x210 [ 1960.795370] Code: 4c 89 f7 e8 ae c8 00 00 eb 22 41 83 c4 02 4c 89 e8 41 83 fc 3f 0f 86 64 ff ff ff 44 89 fe 48 c7 c7 20 94 1e 83 e8 54 ed 81 fe <0f> 0b 48 b8 00 00 00 00 00 fc ff df 48 01 c3 c7 03 00 00 00 00 c7 [ 1960.795402] RSP: 0018:ffff88074d4df7b8 EFLAGS: 00010082 [ 1960.795421] RAX: 0000000000000000 RBX: 1ffff100e9a9befa RCX: ffffffff81479975 [ 1960.795440] RDX: 0000000000000000 RSI: 0000000000000008 RDI: ffff88077c1de690 [ 1960.795460] RBP: ffff88074d4df878 R08: ffffed00ef83bcd3 R09: ffffed00ef83bcd2 [ 1960.795479] R10: ffffed00ef83bcd2 R11: ffff88077c1de697 R12: 000000000000036b [ 1960.795498] R13: 0000000000000202 R14: ffffffffa0aa7fa0 R15: 000000006b6b6b6b [ 1960.795518] FS: 00007f59e0995b80(0000) GS:ffff88077c1c0000(0000) knlGS:0000000000000000 [ 1960.795553] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 1960.795571] CR2: 00007f59e09a2010 CR3: 00000004a1a70005 CR4: 00000000003606e0 [ 1960.795596] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 1960.795629] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [ 1960.795649] Call Trace: [ 1960.795667] ? ida_destroy+0x1d0/0x1d0 [ 1960.795686] ? kasan_check_write+0x14/0x20 [ 1960.795704] ? do_raw_spin_lock+0xc2/0x1c0 [ 1960.795724] ida_simple_remove+0x26/0x40 [ 1960.795794] nouveau_backlight_exit+0x9d/0x150 [nouveau] [ 1960.795867] nouveau_display_destroy+0x76/0x150 [nouveau] [ 1960.795930] nouveau_drm_device_fini+0xb7/0x190 [nouveau] [ 1960.795989] nouveau_drm_device_remove+0x14b/0x1d0 [nouveau] [ 1960.796047] ? nouveau_cli_work_queue+0x2e0/0x2e0 [nouveau] [ 1960.796067] ? trace_hardirqs_on_caller+0x38b/0x570 [ 1960.796089] ? trace_hardirqs_on+0xd/0x10 [ 1960.796146] nouveau_drm_remove+0x37/0x50 [nouveau] [ 1960.796167] pci_device_remove+0x112/0x2d0 [ 1960.796186] ? pcibios_free_irq+0x10/0x10 [ 1960.796218] ? kasan_check_write+0x14/0x20 [ 1960.796237] device_release_driver_internal+0x35c/0x650 [ 1960.796257] device_release_driver+0x12/0x20 [ 1960.796289] pci_stop_bus_device+0x172/0x1e0 [ 1960.796308] pci_stop_and_remove_bus_device_locked+0x1a/0x30 [ 1960.796328] remove_store+0xcb/0xe0 [ 1960.796345] ? sriov_numvfs_store+0x2e0/0x2e0 [ 1960.796364] ? __lock_is_held+0xb5/0x140 [ 1960.796383] ? component_add+0x530/0x530 [ 1960.796401] dev_attr_store+0x3f/0x70 [ 1960.796419] ? sysfs_file_ops+0x11d/0x170 [ 1960.796436] sysfs_kf_write+0x104/0x150 [ 1960.796454] ? sysfs_file_ops+0x170/0x170 [ 1960.796471] kernfs_fop_write+0x24f/0x400 [ 1960.796488] ? __lock_acquire+0x6ea/0x47f0 [ 1960.796520] __vfs_write+0xeb/0x760 [ 1960.796538] ? kernel_read+0x130/0x130 [ 1960.796556] ? __lock_is_held+0xb5/0x140 [ 1960.796590] ? rcu_read_lock_sched_held+0xdd/0x110 [ 1960.796608] ? rcu_sync_lockdep_assert+0x78/0xb0 [ 1960.796626] ? __sb_start_write+0x183/0x220 [ 1960.796648] vfs_write+0x14d/0x4a0 [ 1960.796666] ksys_write+0xd2/0x1b0 [ 1960.796684] ? __ia32_sys_read+0xb0/0xb0 [ 1960.796701] ? fput+0x1d/0x120 [ 1960.796732] ? filp_close+0xf3/0x130 [ 1960.796749] ? entry_SYSCALL_64_after_hwframe+0x59/0xbe [ 1960.796768] __x64_sys_write+0x73/0xb0 [ 1960.796800] do_syscall_64+0xaa/0x400 [ 1960.796818] entry_SYSCALL_64_after_hwframe+0x49/0xbe [ 1960.796836] RIP: 0033:0x7f59df433164 [ 1960.796854] Code: 89 02 48 c7 c0 ff ff ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 66 90 48 8d 05 81 38 2d 00 8b 00 85 c0 75 13 b8 01 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 54 c3 0f 1f 00 41 54 49 89 d4 55 48 89 f5 53 [ 1960.796884] RSP: 002b:00007ffd70ee2fb8 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 [ 1960.796906] RAX: ffffffffffffffda RBX: 0000000000000002 RCX: 00007f59df433164 [ 1960.796926] RDX: 0000000000000002 RSI: 00005578088640c0 RDI: 0000000000000001 [ 1960.796946] RBP: 00005578088640c0 R08: 00007f59df7038c0 R09: 00007f59e0995b80 [ 1960.796966] R10: 000000000000000a R11: 0000000000000246 R12: 00007f59df702760 [ 1960.796985] R13: 0000000000000002 R14: 00007f59df6fd760 R15: 0000000000000002 [ 1960.797008] irq event stamp: 509990 [ 1960.797026] hardirqs last enabled at (509989): [] flush_work+0x4b8/0x6d0 [ 1960.797063] hardirqs last disabled at (509990): [] _raw_spin_lock_irqsave+0x25/0x60 [ 1960.797085] softirqs last enabled at (509744): [] __do_softirq+0x5ad/0x8c0 [ 1960.797121] softirqs last disabled at (509735): [] irq_exit+0x1a5/0x1e0 [ 1960.797142] ---[ end trace fb1342325f1846b8 ]--- While I haven't actually gone into the details of what's causing this to happen (maybe the kernel removes the backlight device in the device core before we get to it?), it doesn't really matter anyway because the way nouveau handles backlights has long since been deprecated. According to the documentation on the drm_connector->late_register() hook, the ->late_register() hook should be used for adding extra connector-related devices. Vice versa, the ->early_unregister() hook is meant to be used for removing those devices. So: gut nouveau_drm->bl_list and nouveau_drm->backlight, and replace them with per-connector backlight structures. Additionally, move backlight registration/teardown into the ->late_register() and ->early_unregister() hooks so that DRM can give us a chance to remove the backlight before the connector is even removed. This appears to fix the problem once and for all. Changes since v2: - Use NV_INFO_ONCE for printing GMUX information, since otherwise this will end up printing that message for as many times as we have connectors Signed-off-by: Lyude Paul Reviewed-by: Karol Herbst Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_backlight.c | 153 +++++++++++--------- drivers/gpu/drm/nouveau/nouveau_connector.c | 20 +++ drivers/gpu/drm/nouveau/nouveau_connector.h | 33 +++++ drivers/gpu/drm/nouveau/nouveau_display.c | 2 - drivers/gpu/drm/nouveau/nouveau_display.h | 25 ---- drivers/gpu/drm/nouveau/nouveau_drv.h | 2 - 6 files changed, 134 insertions(+), 101 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c index 6dd72bc32897..5d0694680f59 100644 --- a/drivers/gpu/drm/nouveau/nouveau_backlight.c +++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c @@ -37,18 +37,19 @@ #include "nouveau_drv.h" #include "nouveau_reg.h" #include "nouveau_encoder.h" +#include "nouveau_connector.h" static struct ida bl_ida; #define BL_NAME_SIZE 15 // 12 for name + 2 for digits + 1 for '\0' -struct backlight_connector { - struct list_head head; +struct nouveau_backlight { + struct backlight_device *dev; int id; }; static bool -nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE], struct backlight_connector - *connector) +nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE], + struct nouveau_backlight *bl) { const int nb = ida_simple_get(&bl_ida, 0, 0, GFP_KERNEL); if (nb < 0 || nb >= 100) @@ -57,7 +58,7 @@ nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE], struct backlight_c snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight%d", nb); else snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight"); - connector->id = nb; + bl->id = nb; return true; } @@ -96,36 +97,45 @@ static int nv40_backlight_init(struct drm_connector *connector) { struct nouveau_drm *drm = nouveau_drm(connector->dev); + struct nouveau_backlight *bl; struct nvif_object *device = &drm->client.device.object; struct backlight_properties props; - struct backlight_device *bd; - struct backlight_connector bl_connector; char backlight_name[BL_NAME_SIZE]; + int ret = 0; if (!(nvif_rd32(device, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK)) return 0; + bl = kzalloc(sizeof(*bl), GFP_KERNEL); + if (!bl) + return -ENOMEM; + memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_RAW; props.max_brightness = 31; - if (!nouveau_get_backlight_name(backlight_name, &bl_connector)) { + if (!nouveau_get_backlight_name(backlight_name, bl)) { NV_ERROR(drm, "Failed to retrieve a unique name for the backlight interface\n"); - return 0; + goto fail_alloc; } - bd = backlight_device_register(backlight_name , connector->kdev, drm, - &nv40_bl_ops, &props); - if (IS_ERR(bd)) { - if (bl_connector.id >= 0) - ida_simple_remove(&bl_ida, bl_connector.id); - return PTR_ERR(bd); + bl->dev = backlight_device_register(backlight_name, connector->kdev, + drm, &nv40_bl_ops, &props); + if (IS_ERR(bl->dev)) { + if (bl->id >= 0) + ida_simple_remove(&bl_ida, bl->id); + + ret = PTR_ERR(bl->dev); + goto fail_alloc; } - list_add(&bl_connector.head, &drm->bl_connectors); - drm->backlight = bd; - bd->props.brightness = nv40_get_intensity(bd); - backlight_update_status(bd); + + nouveau_connector(connector)->backlight = bl; + bl->dev->props.brightness = nv40_get_intensity(bl->dev); + backlight_update_status(bl->dev); return 0; +fail_alloc: + kfree(bl); + return ret; } static int @@ -215,11 +225,11 @@ nv50_backlight_init(struct drm_connector *connector) struct nouveau_drm *drm = nouveau_drm(connector->dev); struct nvif_object *device = &drm->client.device.object; struct nouveau_encoder *nv_encoder; + struct nouveau_backlight *bl; struct backlight_properties props; - struct backlight_device *bd; const struct backlight_ops *ops; - struct backlight_connector bl_connector; char backlight_name[BL_NAME_SIZE]; + int ret = 0; nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS); if (!nv_encoder) { @@ -231,6 +241,10 @@ nv50_backlight_init(struct drm_connector *connector) if (!nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(ffs(nv_encoder->dcb->or) - 1))) return 0; + bl = kzalloc(sizeof(*bl), GFP_KERNEL); + if (!bl) + return -ENOMEM; + if (drm->client.device.info.chipset <= 0xa0 || drm->client.device.info.chipset == 0xaa || drm->client.device.info.chipset == 0xac) @@ -241,79 +255,74 @@ nv50_backlight_init(struct drm_connector *connector) memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_RAW; props.max_brightness = 100; - if (!nouveau_get_backlight_name(backlight_name, &bl_connector)) { + if (!nouveau_get_backlight_name(backlight_name, bl)) { NV_ERROR(drm, "Failed to retrieve a unique name for the backlight interface\n"); - return 0; - } - bd = backlight_device_register(backlight_name , connector->kdev, - nv_encoder, ops, &props); - - if (IS_ERR(bd)) { - if (bl_connector.id >= 0) - ida_simple_remove(&bl_ida, bl_connector.id); - return PTR_ERR(bd); + goto fail_alloc; } - list_add(&bl_connector.head, &drm->bl_connectors); - drm->backlight = bd; - bd->props.brightness = bd->ops->get_brightness(bd); - backlight_update_status(bd); + bl->dev = backlight_device_register(backlight_name, connector->kdev, + nv_encoder, ops, &props); + if (IS_ERR(bl->dev)) { + if (bl->id >= 0) + ida_simple_remove(&bl_ida, bl->id); + + ret = PTR_ERR(bl->dev); + goto fail_alloc; + } + + nouveau_connector(connector)->backlight = bl; + bl->dev->props.brightness = bl->dev->ops->get_brightness(bl->dev); + backlight_update_status(bl->dev); return 0; + +fail_alloc: + kfree(bl); + return ret; } int -nouveau_backlight_init(struct drm_device *dev) +nouveau_backlight_init(struct drm_connector *connector) { - struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_drm *drm = nouveau_drm(connector->dev); struct nvif_device *device = &drm->client.device; - struct drm_connector *connector; - struct drm_connector_list_iter conn_iter; - - INIT_LIST_HEAD(&drm->bl_connectors); if (apple_gmux_present()) { - NV_INFO(drm, "Apple GMUX detected: not registering Nouveau backlight interface\n"); + NV_INFO_ONCE(drm, "Apple GMUX detected: not registering Nouveau backlight interface\n"); return 0; } - drm_connector_list_iter_begin(dev, &conn_iter); - drm_for_each_connector_iter(connector, &conn_iter) { - if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS && - connector->connector_type != DRM_MODE_CONNECTOR_eDP) - continue; + if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS && + connector->connector_type != DRM_MODE_CONNECTOR_eDP) + return 0; - switch (device->info.family) { - case NV_DEVICE_INFO_V0_CURIE: - return nv40_backlight_init(connector); - case NV_DEVICE_INFO_V0_TESLA: - case NV_DEVICE_INFO_V0_FERMI: - case NV_DEVICE_INFO_V0_KEPLER: - case NV_DEVICE_INFO_V0_MAXWELL: - return nv50_backlight_init(connector); - default: - break; - } + switch (device->info.family) { + case NV_DEVICE_INFO_V0_CURIE: + return nv40_backlight_init(connector); + case NV_DEVICE_INFO_V0_TESLA: + case NV_DEVICE_INFO_V0_FERMI: + case NV_DEVICE_INFO_V0_KEPLER: + case NV_DEVICE_INFO_V0_MAXWELL: + return nv50_backlight_init(connector); + default: + return 0; } - drm_connector_list_iter_end(&conn_iter); - - return 0; } void -nouveau_backlight_exit(struct drm_device *dev) +nouveau_backlight_exit(struct drm_connector *connector) { - struct nouveau_drm *drm = nouveau_drm(dev); - struct backlight_connector *connector; + struct nouveau_connector *nv_conn = nouveau_connector(connector); + struct nouveau_backlight *bl = nv_conn->backlight; - list_for_each_entry(connector, &drm->bl_connectors, head) { - if (connector->id >= 0) - ida_simple_remove(&bl_ida, connector->id); - } + if (!bl) + return; - if (drm->backlight) { - backlight_device_unregister(drm->backlight); - drm->backlight = NULL; - } + if (bl->id >= 0) + ida_simple_remove(&bl_ida, bl->id); + + backlight_device_unregister(bl->dev); + nv_conn->backlight = NULL; + kfree(bl); } void diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index dde8724aa8f5..036c6484c4cd 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -885,6 +885,22 @@ nouveau_connector_detect_depth(struct drm_connector *connector) connector->display_info.bpc = 8; } +static int +nouveau_connector_late_register(struct drm_connector *connector) +{ + int ret; + + ret = nouveau_backlight_init(connector); + + return ret; +} + +static void +nouveau_connector_early_unregister(struct drm_connector *connector) +{ + nouveau_backlight_exit(connector); +} + static int nouveau_connector_get_modes(struct drm_connector *connector) { @@ -1069,6 +1085,8 @@ nouveau_connector_funcs = { .atomic_destroy_state = nouveau_conn_atomic_destroy_state, .atomic_set_property = nouveau_conn_atomic_set_property, .atomic_get_property = nouveau_conn_atomic_get_property, + .late_register = nouveau_connector_late_register, + .early_unregister = nouveau_connector_early_unregister, }; static const struct drm_connector_funcs @@ -1084,6 +1102,8 @@ nouveau_connector_funcs_lvds = { .atomic_destroy_state = nouveau_conn_atomic_destroy_state, .atomic_set_property = nouveau_conn_atomic_set_property, .atomic_get_property = nouveau_conn_atomic_get_property, + .late_register = nouveau_connector_late_register, + .early_unregister = nouveau_connector_early_unregister, }; static int diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h index 0acc07555bcd..6f89d4938059 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -39,6 +39,10 @@ struct nvkm_i2c_port; +#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT +struct nouveau_backlight; +#endif + struct nouveau_connector { struct drm_connector base; enum dcb_connector_type type; @@ -55,6 +59,9 @@ struct nouveau_connector { struct nouveau_encoder *detected_encoder; struct edid *edid; struct drm_display_mode *native_mode; +#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT + struct nouveau_backlight *backlight; +#endif }; static inline struct nouveau_connector *nouveau_connector( @@ -181,4 +188,30 @@ int nouveau_conn_atomic_get_property(struct drm_connector *, const struct drm_connector_state *, struct drm_property *, u64 *); struct drm_display_mode *nouveau_conn_native_mode(struct drm_connector *); + +#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT +extern int nouveau_backlight_init(struct drm_connector *); +extern void nouveau_backlight_exit(struct drm_connector *); +extern void nouveau_backlight_ctor(void); +extern void nouveau_backlight_dtor(void); +#else +static inline int +nouveau_backlight_init(struct drm_connector *connector) +{ + return 0; +} + +static inline void +nouveau_backlight_exit(struct drm_connector *connector) { +} + +static inline void +nouveau_backlight_ctor(void) { +} + +static inline void +nouveau_backlight_dtor(void) { +} +#endif + #endif /* __NOUVEAU_CONNECTOR_H__ */ diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 540c0cbbfcee..f326ffd86766 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -582,7 +582,6 @@ nouveau_display_create(struct drm_device *dev) goto vblank_err; } - nouveau_backlight_init(dev); INIT_WORK(&drm->hpd_work, nouveau_display_hpd_work); #ifdef CONFIG_ACPI drm->acpi_nb.notifier_call = nouveau_display_acpi_ntfy; @@ -607,7 +606,6 @@ nouveau_display_destroy(struct drm_device *dev) #ifdef CONFIG_ACPI unregister_acpi_notifier(&nouveau_drm(dev)->acpi_nb); #endif - nouveau_backlight_exit(dev); nouveau_display_vblank_fini(dev); drm_kms_helper_poll_fini(dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_display.h b/drivers/gpu/drm/nouveau/nouveau_display.h index ff92b54ce448..eb77e41c2d4e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.h +++ b/drivers/gpu/drm/nouveau/nouveau_display.h @@ -85,31 +85,6 @@ int nouveau_display_dumb_map_offset(struct drm_file *, struct drm_device *, void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *); -#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT -extern int nouveau_backlight_init(struct drm_device *); -extern void nouveau_backlight_exit(struct drm_device *); -extern void nouveau_backlight_ctor(void); -extern void nouveau_backlight_dtor(void); -#else -static inline int -nouveau_backlight_init(struct drm_device *dev) -{ - return 0; -} - -static inline void -nouveau_backlight_exit(struct drm_device *dev) { -} - -static inline void -nouveau_backlight_ctor(void) { -} - -static inline void -nouveau_backlight_dtor(void) { -} -#endif - struct drm_framebuffer * nouveau_user_framebuffer_create(struct drm_device *, struct drm_file *, const struct drm_mode_fb_cmd2 *); diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index d9d687916553..0b2191fa96f7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -194,8 +194,6 @@ struct nouveau_drm { /* modesetting */ struct nvbios vbios; struct nouveau_display *display; - struct backlight_device *backlight; - struct list_head bl_connectors; struct work_struct hpd_work; struct work_struct fbcon_work; int fbcon_new_state; From a4e05f415e2fb22b1804c86bbf84cc1d5d29b9f1 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Thu, 6 Sep 2018 17:43:24 -0400 Subject: [PATCH 04/14] drm/nouveau/drm/nouveau: s/nouveau_backlight_exit/nouveau_backlight_fini/ More consistent with the rest of the codebase, no functional changes here. Signed-off-by: Lyude Paul Reviewed-by: Karol Herbst Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_backlight.c | 2 +- drivers/gpu/drm/nouveau/nouveau_connector.c | 2 +- drivers/gpu/drm/nouveau/nouveau_connector.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c index 5d0694680f59..f4400a6408b4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_backlight.c +++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c @@ -309,7 +309,7 @@ nouveau_backlight_init(struct drm_connector *connector) } void -nouveau_backlight_exit(struct drm_connector *connector) +nouveau_backlight_fini(struct drm_connector *connector) { struct nouveau_connector *nv_conn = nouveau_connector(connector); struct nouveau_backlight *bl = nv_conn->backlight; diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 036c6484c4cd..a22f4fd03a06 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -898,7 +898,7 @@ nouveau_connector_late_register(struct drm_connector *connector) static void nouveau_connector_early_unregister(struct drm_connector *connector) { - nouveau_backlight_exit(connector); + nouveau_backlight_fini(connector); } static int diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h index 6f89d4938059..f57ef35b1e5e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -191,7 +191,7 @@ struct drm_display_mode *nouveau_conn_native_mode(struct drm_connector *); #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT extern int nouveau_backlight_init(struct drm_connector *); -extern void nouveau_backlight_exit(struct drm_connector *); +extern void nouveau_backlight_fini(struct drm_connector *); extern void nouveau_backlight_ctor(void); extern void nouveau_backlight_dtor(void); #else @@ -202,7 +202,7 @@ nouveau_backlight_init(struct drm_connector *connector) } static inline void -nouveau_backlight_exit(struct drm_connector *connector) { +nouveau_backlight_fini(struct drm_connector *connector) { } static inline void From f76e174bd30a8a43f5c50a33f2988f84ee5d57de Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Thu, 6 Sep 2018 17:43:25 -0400 Subject: [PATCH 05/14] drm/nouveau: Cleanup indenting in nouveau_backlight.c Still no functional changes. Signed-off-by: Lyude Paul Reviewed-by: Karol Herbst Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_backlight.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c index f4400a6408b4..01d08acac2f0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_backlight.c +++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c @@ -68,7 +68,7 @@ nv40_get_intensity(struct backlight_device *bd) struct nouveau_drm *drm = bl_get_data(bd); struct nvif_object *device = &drm->client.device.object; int val = (nvif_rd32(device, NV40_PMC_BACKLIGHT) & - NV40_PMC_BACKLIGHT_MASK) >> 16; + NV40_PMC_BACKLIGHT_MASK) >> 16; return val; } @@ -82,7 +82,7 @@ nv40_set_intensity(struct backlight_device *bd) int reg = nvif_rd32(device, NV40_PMC_BACKLIGHT); nvif_wr32(device, NV40_PMC_BACKLIGHT, - (val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK)); + (val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK)); return 0; } @@ -164,7 +164,7 @@ nv50_set_intensity(struct backlight_device *bd) u32 val = (bd->props.brightness * div) / 100; nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or), - NV50_PDISP_SOR_PWM_CTL_NEW | val); + NV50_PDISP_SOR_PWM_CTL_NEW | val); return 0; } @@ -204,9 +204,10 @@ nva3_set_intensity(struct backlight_device *bd) div = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or)); val = (bd->props.brightness * div) / 100; if (div) { - nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or), val | - NV50_PDISP_SOR_PWM_CTL_NEW | - NVA3_PDISP_SOR_PWM_CTL_UNK); + nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or), + val | + NV50_PDISP_SOR_PWM_CTL_NEW | + NVA3_PDISP_SOR_PWM_CTL_UNK); return 0; } From e15e4c13e5b77e8b8e8c5e6d23831b1180249788 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Thu, 6 Sep 2018 17:43:26 -0400 Subject: [PATCH 06/14] drm/nouveau: Refactor nvXX_backlight_init() There's literally no difference between any of the backlight init functions besides the backlight properties they set and the backlight callbacks that they set, so move all of the duplicated backlight init code out of there and into nouveau_backlight_init(). This gets rid of a lot of copy pasta! Changes since v1: - Some of the pre-refactor callbacks were storing nv_encoder in callback data for the backlight devices that they registered, as opposed to nouveau_drm. This got missed and caused some bugs that didn't originally appear on my setup (NULL kernel derefs) for some reason. So, fix this by finding the nouveau_encoder in nouveau_backlight_init(), and using that as the callback data for all gens instead even if they don't care about the encoder. Signed-off-by: Lyude Paul Cc: Jeffery Miller Cc: Karol Herbst Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_backlight.c | 168 +++++++++----------- 1 file changed, 76 insertions(+), 92 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c index 01d08acac2f0..5f5be6368aed 100644 --- a/drivers/gpu/drm/nouveau/nouveau_backlight.c +++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c @@ -65,7 +65,8 @@ nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE], static int nv40_get_intensity(struct backlight_device *bd) { - struct nouveau_drm *drm = bl_get_data(bd); + struct nouveau_encoder *nv_encoder = bl_get_data(bd); + struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); struct nvif_object *device = &drm->client.device.object; int val = (nvif_rd32(device, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK) >> 16; @@ -76,7 +77,8 @@ nv40_get_intensity(struct backlight_device *bd) static int nv40_set_intensity(struct backlight_device *bd) { - struct nouveau_drm *drm = bl_get_data(bd); + struct nouveau_encoder *nv_encoder = bl_get_data(bd); + struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); struct nvif_object *device = &drm->client.device.object; int val = bd->props.brightness; int reg = nvif_rd32(device, NV40_PMC_BACKLIGHT); @@ -94,48 +96,20 @@ static const struct backlight_ops nv40_bl_ops = { }; static int -nv40_backlight_init(struct drm_connector *connector) +nv40_backlight_init(struct nouveau_encoder *encoder, + struct backlight_properties *props, + const struct backlight_ops **ops) { - struct nouveau_drm *drm = nouveau_drm(connector->dev); - struct nouveau_backlight *bl; + struct nouveau_drm *drm = nouveau_drm(encoder->base.base.dev); struct nvif_object *device = &drm->client.device.object; - struct backlight_properties props; - char backlight_name[BL_NAME_SIZE]; - int ret = 0; if (!(nvif_rd32(device, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK)) - return 0; - - bl = kzalloc(sizeof(*bl), GFP_KERNEL); - if (!bl) - return -ENOMEM; - - memset(&props, 0, sizeof(struct backlight_properties)); - props.type = BACKLIGHT_RAW; - props.max_brightness = 31; - if (!nouveau_get_backlight_name(backlight_name, bl)) { - NV_ERROR(drm, "Failed to retrieve a unique name for the backlight interface\n"); - goto fail_alloc; - } - - bl->dev = backlight_device_register(backlight_name, connector->kdev, - drm, &nv40_bl_ops, &props); - if (IS_ERR(bl->dev)) { - if (bl->id >= 0) - ida_simple_remove(&bl_ida, bl->id); - - ret = PTR_ERR(bl->dev); - goto fail_alloc; - } - - nouveau_connector(connector)->backlight = bl; - bl->dev->props.brightness = nv40_get_intensity(bl->dev); - backlight_update_status(bl->dev); + return -ENODEV; + props->type = BACKLIGHT_RAW; + props->max_brightness = 31; + *ops = &nv40_bl_ops; return 0; -fail_alloc: - kfree(bl); - return ret; } static int @@ -221,41 +195,79 @@ static const struct backlight_ops nva3_bl_ops = { }; static int -nv50_backlight_init(struct drm_connector *connector) +nv50_backlight_init(struct nouveau_encoder *nv_encoder, + struct backlight_properties *props, + const struct backlight_ops **ops) { - struct nouveau_drm *drm = nouveau_drm(connector->dev); + struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); struct nvif_object *device = &drm->client.device.object; - struct nouveau_encoder *nv_encoder; - struct nouveau_backlight *bl; - struct backlight_properties props; - const struct backlight_ops *ops; - char backlight_name[BL_NAME_SIZE]; - int ret = 0; - - nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS); - if (!nv_encoder) { - nv_encoder = find_encoder(connector, DCB_OUTPUT_DP); - if (!nv_encoder) - return -ENODEV; - } if (!nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(ffs(nv_encoder->dcb->or) - 1))) + return -ENODEV; + + if (drm->client.device.info.chipset <= 0xa0 || + drm->client.device.info.chipset == 0xaa || + drm->client.device.info.chipset == 0xac) + *ops = &nv50_bl_ops; + else + *ops = &nva3_bl_ops; + + props->type = BACKLIGHT_RAW; + props->max_brightness = 100; + + return 0; +} + +int +nouveau_backlight_init(struct drm_connector *connector) +{ + struct nouveau_drm *drm = nouveau_drm(connector->dev); + struct nouveau_backlight *bl; + struct nouveau_encoder *nv_encoder = NULL; + struct nvif_device *device = &drm->client.device; + char backlight_name[BL_NAME_SIZE]; + struct backlight_properties props = {0}; + const struct backlight_ops *ops; + int ret; + + if (apple_gmux_present()) { + NV_INFO_ONCE(drm, "Apple GMUX detected: not registering Nouveau backlight interface\n"); return 0; + } + + if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) + nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS); + else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) + nv_encoder = find_encoder(connector, DCB_OUTPUT_DP); + else + return 0; + + if (!nv_encoder) + return 0; + + switch (device->info.family) { + case NV_DEVICE_INFO_V0_CURIE: + ret = nv40_backlight_init(nv_encoder, &props, &ops); + break; + case NV_DEVICE_INFO_V0_TESLA: + case NV_DEVICE_INFO_V0_FERMI: + case NV_DEVICE_INFO_V0_KEPLER: + case NV_DEVICE_INFO_V0_MAXWELL: + ret = nv50_backlight_init(nv_encoder, &props, &ops); + break; + default: + return 0; + } + + if (ret == -ENODEV) + return 0; + else if (ret) + return ret; bl = kzalloc(sizeof(*bl), GFP_KERNEL); if (!bl) return -ENOMEM; - if (drm->client.device.info.chipset <= 0xa0 || - drm->client.device.info.chipset == 0xaa || - drm->client.device.info.chipset == 0xac) - ops = &nv50_bl_ops; - else - ops = &nva3_bl_ops; - - memset(&props, 0, sizeof(struct backlight_properties)); - props.type = BACKLIGHT_RAW; - props.max_brightness = 100; if (!nouveau_get_backlight_name(backlight_name, bl)) { NV_ERROR(drm, "Failed to retrieve a unique name for the backlight interface\n"); goto fail_alloc; @@ -266,7 +278,6 @@ nv50_backlight_init(struct drm_connector *connector) if (IS_ERR(bl->dev)) { if (bl->id >= 0) ida_simple_remove(&bl_ida, bl->id); - ret = PTR_ERR(bl->dev); goto fail_alloc; } @@ -274,6 +285,7 @@ nv50_backlight_init(struct drm_connector *connector) nouveau_connector(connector)->backlight = bl; bl->dev->props.brightness = bl->dev->ops->get_brightness(bl->dev); backlight_update_status(bl->dev); + return 0; fail_alloc: @@ -281,34 +293,6 @@ nv50_backlight_init(struct drm_connector *connector) return ret; } -int -nouveau_backlight_init(struct drm_connector *connector) -{ - struct nouveau_drm *drm = nouveau_drm(connector->dev); - struct nvif_device *device = &drm->client.device; - - if (apple_gmux_present()) { - NV_INFO_ONCE(drm, "Apple GMUX detected: not registering Nouveau backlight interface\n"); - return 0; - } - - if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS && - connector->connector_type != DRM_MODE_CONNECTOR_eDP) - return 0; - - switch (device->info.family) { - case NV_DEVICE_INFO_V0_CURIE: - return nv40_backlight_init(connector); - case NV_DEVICE_INFO_V0_TESLA: - case NV_DEVICE_INFO_V0_FERMI: - case NV_DEVICE_INFO_V0_KEPLER: - case NV_DEVICE_INFO_V0_MAXWELL: - return nv50_backlight_init(connector); - default: - return 0; - } -} - void nouveau_backlight_fini(struct drm_connector *connector) { From c4cee69a4497d9c6ad8868d63568b30e50cac9e9 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Wed, 22 Aug 2018 21:40:06 -0400 Subject: [PATCH 07/14] drm/nouveau: Fix potential memory leak in nouveau_drm_load() We forget to free drm in all instances of failure, and additionally also forget to destroy the master client if the other client fails initialization. Signed-off-by: Lyude Paul Cc: Karol Herbst Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drm.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 74d2283f2c28..905956809d21 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -538,11 +538,11 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags) ret = nouveau_cli_init(drm, "DRM-master", &drm->master); if (ret) - return ret; + goto fail_alloc; ret = nouveau_cli_init(drm, "DRM", &drm->client); if (ret) - return ret; + goto fail_master; dev->irq_enabled = true; @@ -605,7 +605,9 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags) fail_ttm: nouveau_vga_fini(drm); nouveau_cli_fini(&drm->client); +fail_master: nouveau_cli_fini(&drm->master); +fail_alloc: kfree(drm); return ret; } From cfea88a4d86632f28cf80be97079f131645b7869 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Wed, 22 Aug 2018 21:40:07 -0400 Subject: [PATCH 08/14] drm/nouveau: Start using new drm_dev initialization helpers Per the documentation in drm_get_pci_dev(), this function is deprecated and shouldn't be used anymore. As it turns out, we're going to need to stop using drm_get_pci_dev() anyway in order to allow us to turn off the card before full system shutdowns, otherwise we'll hit race conditions with userspace while trying to tear down the card on shutdown. So, start using drm_dev_get() and drm_dev_put(), and just turn our load/unload callbacks into open coded init/fini() functions. Signed-off-by: Lyude Paul Cc: Karol Herbst Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drm.c | 173 +++++++++++++++----------- 1 file changed, 101 insertions(+), 72 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 905956809d21..2b2baf6e0e0d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -458,75 +458,8 @@ nouveau_accel_init(struct nouveau_drm *drm) nouveau_bo_move_init(drm); } -static int nouveau_drm_probe(struct pci_dev *pdev, - const struct pci_device_id *pent) -{ - struct nvkm_device *device; - struct apertures_struct *aper; - bool boot = false; - int ret; - - if (vga_switcheroo_client_probe_defer(pdev)) - return -EPROBE_DEFER; - - /* We need to check that the chipset is supported before booting - * fbdev off the hardware, as there's no way to put it back. - */ - ret = nvkm_device_pci_new(pdev, NULL, "error", true, false, 0, &device); - if (ret) - return ret; - - nvkm_device_del(&device); - - /* Remove conflicting drivers (vesafb, efifb etc). */ - aper = alloc_apertures(3); - if (!aper) - return -ENOMEM; - - aper->ranges[0].base = pci_resource_start(pdev, 1); - aper->ranges[0].size = pci_resource_len(pdev, 1); - aper->count = 1; - - if (pci_resource_len(pdev, 2)) { - aper->ranges[aper->count].base = pci_resource_start(pdev, 2); - aper->ranges[aper->count].size = pci_resource_len(pdev, 2); - aper->count++; - } - - if (pci_resource_len(pdev, 3)) { - aper->ranges[aper->count].base = pci_resource_start(pdev, 3); - aper->ranges[aper->count].size = pci_resource_len(pdev, 3); - aper->count++; - } - -#ifdef CONFIG_X86 - boot = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; -#endif - if (nouveau_modeset != 2) - drm_fb_helper_remove_conflicting_framebuffers(aper, "nouveaufb", boot); - kfree(aper); - - ret = nvkm_device_pci_new(pdev, nouveau_config, nouveau_debug, - true, true, ~0ULL, &device); - if (ret) - return ret; - - pci_set_master(pdev); - - if (nouveau_atomic) - driver_pci.driver_features |= DRIVER_ATOMIC; - - ret = drm_get_pci_dev(pdev, pent, &driver_pci); - if (ret) { - nvkm_device_del(&device); - return ret; - } - - return 0; -} - static int -nouveau_drm_load(struct drm_device *dev, unsigned long flags) +nouveau_drm_device_init(struct drm_device *dev) { struct nouveau_drm *drm; int ret; @@ -613,7 +546,7 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags) } static void -nouveau_drm_unload(struct drm_device *dev) +nouveau_drm_device_fini(struct drm_device *dev) { struct nouveau_drm *drm = nouveau_drm(dev); @@ -642,18 +575,116 @@ nouveau_drm_unload(struct drm_device *dev) kfree(drm); } +static int nouveau_drm_probe(struct pci_dev *pdev, + const struct pci_device_id *pent) +{ + struct nvkm_device *device; + struct drm_device *drm_dev; + struct apertures_struct *aper; + bool boot = false; + int ret; + + if (vga_switcheroo_client_probe_defer(pdev)) + return -EPROBE_DEFER; + + /* We need to check that the chipset is supported before booting + * fbdev off the hardware, as there's no way to put it back. + */ + ret = nvkm_device_pci_new(pdev, NULL, "error", true, false, 0, &device); + if (ret) + return ret; + + nvkm_device_del(&device); + + /* Remove conflicting drivers (vesafb, efifb etc). */ + aper = alloc_apertures(3); + if (!aper) + return -ENOMEM; + + aper->ranges[0].base = pci_resource_start(pdev, 1); + aper->ranges[0].size = pci_resource_len(pdev, 1); + aper->count = 1; + + if (pci_resource_len(pdev, 2)) { + aper->ranges[aper->count].base = pci_resource_start(pdev, 2); + aper->ranges[aper->count].size = pci_resource_len(pdev, 2); + aper->count++; + } + + if (pci_resource_len(pdev, 3)) { + aper->ranges[aper->count].base = pci_resource_start(pdev, 3); + aper->ranges[aper->count].size = pci_resource_len(pdev, 3); + aper->count++; + } + +#ifdef CONFIG_X86 + boot = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; +#endif + if (nouveau_modeset != 2) + drm_fb_helper_remove_conflicting_framebuffers(aper, "nouveaufb", boot); + kfree(aper); + + ret = nvkm_device_pci_new(pdev, nouveau_config, nouveau_debug, + true, true, ~0ULL, &device); + if (ret) + return ret; + + pci_set_master(pdev); + + if (nouveau_atomic) + driver_pci.driver_features |= DRIVER_ATOMIC; + + drm_dev = drm_dev_alloc(&driver_pci, &pdev->dev); + if (IS_ERR(drm_dev)) { + ret = PTR_ERR(drm_dev); + goto fail_nvkm; + } + + ret = pci_enable_device(pdev); + if (ret) + goto fail_drm; + + drm_dev->pdev = pdev; + pci_set_drvdata(pdev, drm_dev); + + ret = nouveau_drm_device_init(drm_dev); + if (ret) + goto fail_pci; + + ret = drm_dev_register(drm_dev, pent->driver_data); + if (ret) + goto fail_drm_dev_init; + + return 0; + +fail_drm_dev_init: + nouveau_drm_device_fini(drm_dev); +fail_pci: + pci_disable_device(pdev); +fail_drm: + drm_dev_put(drm_dev); +fail_nvkm: + nvkm_device_del(&device); + return ret; +} + void nouveau_drm_device_remove(struct drm_device *dev) { + struct pci_dev *pdev = dev->pdev; struct nouveau_drm *drm = nouveau_drm(dev); struct nvkm_client *client; struct nvkm_device *device; + drm_dev_unregister(dev); + dev->irq_enabled = false; client = nvxx_client(&drm->client.base); device = nvkm_device_find(client->device); - drm_put_dev(dev); + nouveau_drm_device_fini(dev); + pci_disable_device(pdev); + drm_dev_put(dev); nvkm_device_del(&device); } @@ -1020,8 +1051,6 @@ driver_stub = { DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_RENDER | DRIVER_KMS_LEGACY_CONTEXT, - .load = nouveau_drm_load, - .unload = nouveau_drm_unload, .open = nouveau_drm_open, .postclose = nouveau_drm_postclose, .lastclose = nouveau_vga_lastclose, From 4126b99e744b7a29746e201e2be6644d2edf3c56 Mon Sep 17 00:00:00 2001 From: Ilia Mirkin Date: Mon, 3 Sep 2018 20:57:33 -0400 Subject: [PATCH 09/14] drm/nouveau/disp: add a way to configure scrambling/tmds for hdmi 2.0 High pixel clocks are required to use a 40 TMDS divider instead of 10, and even low ones may optionally use scrambling depending on device support. Signed-off-by: Ilia Mirkin Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/include/nvif/cl5070.h | 5 ++++- drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h | 1 + drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c | 10 ++++++++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/nouveau/include/nvif/cl5070.h b/drivers/gpu/drm/nouveau/include/nvif/cl5070.h index 7cdf53615d7b..bced81987269 100644 --- a/drivers/gpu/drm/nouveau/include/nvif/cl5070.h +++ b/drivers/gpu/drm/nouveau/include/nvif/cl5070.h @@ -69,7 +69,10 @@ struct nv50_disp_sor_hdmi_pwr_v0 { __u8 rekey; __u8 avi_infoframe_length; __u8 vendor_infoframe_length; - __u8 pad06[2]; +#define NV50_DISP_SOR_HDMI_PWR_V0_SCDC_SCRAMBLE (1 << 0) +#define NV50_DISP_SOR_HDMI_PWR_V0_SCDC_DIV_BY_4 (1 << 1) + __u8 scdc; + __u8 pad07[1]; }; struct nv50_disp_sor_lvds_script_v0 { diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h index 19911211a12a..aa7aa2f21cf6 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h @@ -61,6 +61,7 @@ struct nvkm_ior_func { 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); + void (*scdc)(struct nvkm_ior *, int head, u8 scdc); } hdmi; struct { diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c index 3aa5a2879239..5f758948d6e1 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c @@ -176,9 +176,10 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) 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", + "max_ac_packet %d rekey %d scdc %d\n", args->v0.version, args->v0.state, - args->v0.max_ac_packet, args->v0.rekey); + args->v0.max_ac_packet, args->v0.rekey, + args->v0.scdc); if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f) return -EINVAL; if ((args->v0.avi_infoframe_length @@ -202,6 +203,11 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) args->v0.max_ac_packet, args->v0.rekey, avi, avi_size, vendor, vendor_size); + + if (outp->ior->func->hdmi.scdc) + outp->ior->func->hdmi.scdc( + outp->ior, hidx, args->v0.scdc); + return 0; } break; From 4834e05049c9c807c38d0727ab422898fdfe2996 Mon Sep 17 00:00:00 2001 From: Ilia Mirkin Date: Mon, 3 Sep 2018 20:57:34 -0400 Subject: [PATCH 10/14] drm/nouveau/disp/gm200-: add scdc parameter setter Signed-off-by: Ilia Mirkin Signed-off-by: Ben Skeggs --- .../gpu/drm/nouveau/nvkm/engine/disp/Kbuild | 1 + .../drm/nouveau/nvkm/engine/disp/hdmigm200.c | 34 +++++++++++++++++++ .../gpu/drm/nouveau/nvkm/engine/disp/ior.h | 2 ++ .../drm/nouveau/nvkm/engine/disp/sorgm200.c | 1 + .../drm/nouveau/nvkm/engine/disp/sorgv100.c | 1 + 5 files changed, 39 insertions(+) create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigm200.c diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild index 3d485dbf310a..8089ac9a12e2 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild @@ -50,6 +50,7 @@ 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/hdmigm200.o nvkm-y += nvkm/engine/disp/hdmigv100.o nvkm-y += nvkm/engine/disp/conn.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigm200.c new file mode 100644 index 000000000000..ad5f658c3f6d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigm200.c @@ -0,0 +1,34 @@ +/* + * Copyright 2018 Ilia Mirkin + * + * 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: Ilia Mirkin + */ +#include "hdmi.h" + +void +gm200_hdmi_scdc(struct nvkm_ior *ior, int head, u8 scdc) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 hoff = head * 0x800; + const u32 ctrl = scdc & 0x3; + + nvkm_mask(device, 0x61c5bc + hoff, 0x00000003, ctrl); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h index aa7aa2f21cf6..c5d34424f45f 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h @@ -145,6 +145,8 @@ 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 gv100_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8); +void gm200_hdmi_scdc(struct nvkm_ior *, int, u8); + void gt215_hda_hpd(struct nvkm_ior *, int, bool); void gt215_hda_eld(struct nvkm_ior *, u8 *, u8); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c index d892bdf04034..384f82652bec 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c @@ -99,6 +99,7 @@ gm200_sor = { .clock = gf119_sor_clock, .hdmi = { .ctrl = gk104_hdmi_ctrl, + .scdc = gm200_hdmi_scdc, }, .dp = { .lanes = { 0, 1, 2, 3 }, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgv100.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgv100.c index 040db8a338de..8ba881a729ee 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgv100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgv100.c @@ -88,6 +88,7 @@ gv100_sor = { .clock = gf119_sor_clock, .hdmi = { .ctrl = gv100_hdmi_ctrl, + .scdc = gm200_hdmi_scdc, }, .dp = { .lanes = { 0, 1, 2, 3 }, From a971558c298755d2c07bc5508c65d689471763c8 Mon Sep 17 00:00:00 2001 From: Ilia Mirkin Date: Mon, 3 Sep 2018 20:57:35 -0400 Subject: [PATCH 11/14] drm/nouveau/disp: keep track of high-speed state, program into clock The register programmed by the clock method needs to contain a different setting for the link speed as well as special divider settings. Signed-off-by: Ilia Mirkin Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigm200.c | 2 ++ drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h | 5 +++++ drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c | 11 +++++++---- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigm200.c index ad5f658c3f6d..9b16a08eb4d9 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigm200.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigm200.c @@ -31,4 +31,6 @@ gm200_hdmi_scdc(struct nvkm_ior *ior, int head, u8 scdc) const u32 ctrl = scdc & 0x3; nvkm_mask(device, 0x61c5bc + hoff, 0x00000003, ctrl); + + ior->tmds.high_speed = !!(scdc & 0x2); } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h index c5d34424f45f..0f0c86c32ec3 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h @@ -41,6 +41,11 @@ struct nvkm_ior { u8 nr; u8 bw; } dp; + + /* Armed TMDS state. */ + struct { + bool high_speed; + } tmds; }; struct nvkm_ior_func { diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c index e6e6dfbb1283..456a5a143522 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c @@ -120,13 +120,16 @@ void gf119_sor_clock(struct nvkm_ior *sor) { struct nvkm_device *device = sor->disp->engine.subdev.device; - const int div = sor->asy.link == 3; const u32 soff = nv50_ior_base(sor); + u32 div1 = sor->asy.link == 3; + u32 div2 = sor->asy.link == 3; 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); + const u32 speed = sor->tmds.high_speed ? 0x14 : 0x0a; + nvkm_mask(device, 0x612300 + soff, 0x007c0000, speed << 18); + if (sor->tmds.high_speed) + div2 = 1; } - nvkm_mask(device, 0x612300 + soff, 0x00000707, (div << 8) | div); + nvkm_mask(device, 0x612300 + soff, 0x00000707, (div2 << 8) | div1); } void From 7a406f8a62ff0a3647f96f0cfdb518a99a01bf3f Mon Sep 17 00:00:00 2001 From: Ilia Mirkin Date: Mon, 3 Sep 2018 20:57:36 -0400 Subject: [PATCH 12/14] drm/nouveau/disp: add support for setting scdc parameters for high modes When SCDC is supported, make sure that we configure the GPU and monitor to the same parameters. Signed-off-by: Ilia Mirkin Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/dispnv50/disp.c | 40 ++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 1dbd1dcdcf15..5f163a025e89 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -531,6 +532,7 @@ nv50_hdmi_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc) static void nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) { + struct nouveau_drm *drm = nouveau_drm(encoder->dev); struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); struct nv50_disp *disp = nv50_disp(encoder->dev); @@ -548,9 +550,12 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) .pwr.rekey = 56, /* binary driver, and tegra, constant */ }; struct nouveau_connector *nv_connector; + struct drm_hdmi_info *hdmi; u32 max_ac_packet; union hdmi_infoframe avi_frame; union hdmi_infoframe vendor_frame; + bool scdc_supported, high_tmds_clock_ratio = false, scrambling = false; + u8 config; int ret; int size; @@ -558,8 +563,11 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) if (!drm_detect_hdmi_monitor(nv_connector->edid)) return; + hdmi = &nv_connector->base.display_info.hdmi; + scdc_supported = hdmi->scdc.supported; + ret = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame.avi, mode, - false); + scdc_supported); if (!ret) { /* We have an AVI InfoFrame, populate it to the display */ args.pwr.avi_infoframe_length @@ -582,12 +590,42 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) max_ac_packet -= 18; /* constant from tegra */ args.pwr.max_ac_packet = max_ac_packet / 32; + if (hdmi->scdc.scrambling.supported) { + high_tmds_clock_ratio = mode->clock > 340000; + scrambling = high_tmds_clock_ratio || + hdmi->scdc.scrambling.low_rates; + } + + args.pwr.scdc = + NV50_DISP_SOR_HDMI_PWR_V0_SCDC_SCRAMBLE * scrambling | + NV50_DISP_SOR_HDMI_PWR_V0_SCDC_DIV_BY_4 * high_tmds_clock_ratio; + size = sizeof(args.base) + sizeof(args.pwr) + args.pwr.avi_infoframe_length + args.pwr.vendor_infoframe_length; nvif_mthd(&disp->disp->object, 0, &args, size); + nv50_audio_enable(encoder, mode); + + /* If SCDC is supported by the downstream monitor, update + * divider / scrambling settings to what we programmed above. + */ + if (!hdmi->scdc.scrambling.supported) + return; + + ret = drm_scdc_readb(nv_encoder->i2c, SCDC_TMDS_CONFIG, &config); + if (ret < 0) { + NV_ERROR(drm, "Failure to read SCDC_TMDS_CONFIG: %d\n", ret); + return; + } + config &= ~(SCDC_TMDS_BIT_CLOCK_RATIO_BY_40 | SCDC_SCRAMBLING_ENABLE); + config |= SCDC_TMDS_BIT_CLOCK_RATIO_BY_40 * high_tmds_clock_ratio; + config |= SCDC_SCRAMBLING_ENABLE * scrambling; + ret = drm_scdc_writeb(nv_encoder->i2c, SCDC_TMDS_CONFIG, config); + if (ret < 0) + NV_ERROR(drm, "Failure to write SCDC_TMDS_CONFIG = 0x%02x: %d\n", + config, ret); } /****************************************************************************** From 9340d77f5327ea673a7f95f58139123d7a278243 Mon Sep 17 00:00:00 2001 From: Ilia Mirkin Date: Mon, 3 Sep 2018 20:57:37 -0400 Subject: [PATCH 13/14] drm/nouveau/disp: take sink support into account for exposing 594mhz Scrambling is required for supporting any mode over 340MHz. If it's not supported, reject any modes that would require it. Signed-off-by: Ilia Mirkin Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 34 +++++++++++++-------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index a22f4fd03a06..fd80661dff92 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -969,18 +969,33 @@ nouveau_connector_get_modes(struct drm_connector *connector) } static unsigned -get_tmds_link_bandwidth(struct drm_connector *connector, bool hdmi) +get_tmds_link_bandwidth(struct drm_connector *connector) { struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; struct nouveau_drm *drm = nouveau_drm(connector->dev); struct dcb_output *dcb = nv_connector->detected_encoder->dcb; + struct drm_display_info *info = NULL; + const unsigned duallink_scale = + nouveau_duallink && nv_encoder->dcb->duallink_possible ? 2 : 1; - if (hdmi) { + if (drm_detect_hdmi_monitor(nv_connector->edid)) + info = &nv_connector->base.display_info; + + if (info) { if (nouveau_hdmimhz > 0) return nouveau_hdmimhz * 1000; /* Note: these limits are conservative, some Fermi's * can do 297 MHz. Unclear how this can be determined. */ + if (drm->client.device.info.chipset >= 0x120) { + const int max_tmds_clock = + info->hdmi.scdc.scrambling.supported ? + 594000 : 340000; + return info->max_tmds_clock ? + min(info->max_tmds_clock, max_tmds_clock) : + max_tmds_clock; + } if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_KEPLER) return 297000; if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_FERMI) @@ -988,13 +1003,13 @@ get_tmds_link_bandwidth(struct drm_connector *connector, bool hdmi) } if (dcb->location != DCB_LOC_ON_CHIP || drm->client.device.info.chipset >= 0x46) - return 165000; + return 165000 * duallink_scale; else if (drm->client.device.info.chipset >= 0x40) - return 155000; + return 155000 * duallink_scale; else if (drm->client.device.info.chipset >= 0x18) - return 135000; + return 135000 * duallink_scale; else - return 112000; + return 112000 * duallink_scale; } static enum drm_mode_status @@ -1006,7 +1021,6 @@ nouveau_connector_mode_valid(struct drm_connector *connector, struct drm_encoder *encoder = to_drm_encoder(nv_encoder); unsigned min_clock = 25000, max_clock = min_clock; unsigned clock = mode->clock; - bool hdmi; switch (nv_encoder->dcb->type) { case DCB_OUTPUT_LVDS: @@ -1019,11 +1033,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector, max_clock = 400000; break; case DCB_OUTPUT_TMDS: - hdmi = drm_detect_hdmi_monitor(nv_connector->edid); - max_clock = get_tmds_link_bandwidth(connector, hdmi); - if (!hdmi && nouveau_duallink && - nv_encoder->dcb->duallink_possible) - max_clock *= 2; + max_clock = get_tmds_link_bandwidth(connector); break; case DCB_OUTPUT_ANALOG: max_clock = nv_encoder->dcb->crtconf.maxfreq; From 74a07c0a59fa372b069d879971ba4d9e341979cf Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 24 Jul 2018 08:27:19 -0500 Subject: [PATCH 14/14] drm/nouveau/secboot/acr: fix memory leak In case memory resources for *bl_desc* were allocated, release them before return. Addresses-Coverity-ID: 1472021 ("Resource leak") Fixes: 0d466901552a ("drm/nouveau/secboot/acr: Remove VLA usage") Signed-off-by: Gustavo A. R. Silva Reviewed-by: John Hubbard Reviewed-by: Kees Cook Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c index d02e183717dc..5c14d6ac855d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c @@ -801,6 +801,7 @@ acr_r352_load(struct nvkm_acr *_acr, struct nvkm_falcon *falcon, bl = acr->hsbl_unload_blob; } else { nvkm_error(_acr->subdev, "invalid secure boot blob!\n"); + kfree(bl_desc); return -EINVAL; }