From 28fae81f93d5482f25bb8e9881104ad1157c2cfd Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 2 Dec 2015 17:24:20 +0100 Subject: [PATCH 01/14] gpu: host1x: Use platform_register/unregister_drivers() These new helpers simplify implementing multi-driver modules and properly handle failure to register one driver by unregistering all previously registered drivers. Signed-off-by: Thierry Reding --- drivers/gpu/host1x/dev.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c index 53d3d1d45b48..64d9b707ee1a 100644 --- a/drivers/gpu/host1x/dev.c +++ b/drivers/gpu/host1x/dev.c @@ -212,6 +212,11 @@ static struct platform_driver tegra_host1x_driver = { .remove = host1x_remove, }; +static struct platform_driver * const drivers[] = { + &tegra_host1x_driver, + &tegra_mipi_driver, +}; + static int __init tegra_host1x_init(void) { int err; @@ -220,28 +225,17 @@ static int __init tegra_host1x_init(void) if (err < 0) return err; - err = platform_driver_register(&tegra_host1x_driver); + err = platform_register_drivers(drivers, ARRAY_SIZE(drivers)); if (err < 0) - goto unregister_bus; + bus_unregister(&host1x_bus_type); - err = platform_driver_register(&tegra_mipi_driver); - if (err < 0) - goto unregister_host1x; - - return 0; - -unregister_host1x: - platform_driver_unregister(&tegra_host1x_driver); -unregister_bus: - bus_unregister(&host1x_bus_type); return err; } module_init(tegra_host1x_init); static void __exit tegra_host1x_exit(void) { - platform_driver_unregister(&tegra_mipi_driver); - platform_driver_unregister(&tegra_host1x_driver); + platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); bus_unregister(&host1x_bus_type); } module_exit(tegra_host1x_exit); From e3e70814cebb0beec47dd38e60f89850950a2903 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 24 Aug 2015 14:51:04 +0200 Subject: [PATCH 02/14] gpu: host1x: Remove core driver on unregister When unregistering a host1x driver, make sure to unregister the core driver as well to prevent it from sticking around and oppose reloading of the driver. Signed-off-by: Thierry Reding --- drivers/gpu/host1x/bus.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c index 4a99c6416e6a..da462afcb225 100644 --- a/drivers/gpu/host1x/bus.c +++ b/drivers/gpu/host1x/bus.c @@ -538,6 +538,8 @@ EXPORT_SYMBOL(host1x_driver_register_full); void host1x_driver_unregister(struct host1x_driver *driver) { + driver_unregister(&driver->driver); + mutex_lock(&drivers_lock); list_del_init(&driver->list); mutex_unlock(&drivers_lock); From a134789a67480e6cc7e50c9dfcbc7adca5016010 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 23 Mar 2015 10:46:28 +0100 Subject: [PATCH 03/14] gpu: host1x: Add Tegra210 support The host1x unit found in Tegra210 SoCs is very similar to the unit in Tegra124, but it has 2 additional channels for a total of 14 channels. Signed-off-by: Thierry Reding --- drivers/gpu/host1x/Makefile | 3 +- drivers/gpu/host1x/dev.c | 11 + drivers/gpu/host1x/hw/host1x05.c | 42 ++++ drivers/gpu/host1x/hw/host1x05.h | 26 +++ drivers/gpu/host1x/hw/host1x05_hardware.h | 142 ++++++++++++ drivers/gpu/host1x/hw/hw_host1x05_channel.h | 121 ++++++++++ drivers/gpu/host1x/hw/hw_host1x05_sync.h | 243 ++++++++++++++++++++ drivers/gpu/host1x/hw/hw_host1x05_uclass.h | 181 +++++++++++++++ 8 files changed, 768 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/host1x/hw/host1x05.c create mode 100644 drivers/gpu/host1x/hw/host1x05.h create mode 100644 drivers/gpu/host1x/hw/host1x05_hardware.h create mode 100644 drivers/gpu/host1x/hw/hw_host1x05_channel.h create mode 100644 drivers/gpu/host1x/hw/hw_host1x05_sync.h create mode 100644 drivers/gpu/host1x/hw/hw_host1x05_uclass.h diff --git a/drivers/gpu/host1x/Makefile b/drivers/gpu/host1x/Makefile index c1189f004441..a1d9974cfcb5 100644 --- a/drivers/gpu/host1x/Makefile +++ b/drivers/gpu/host1x/Makefile @@ -10,6 +10,7 @@ host1x-y = \ mipi.o \ hw/host1x01.o \ hw/host1x02.o \ - hw/host1x04.o + hw/host1x04.o \ + hw/host1x05.o obj-$(CONFIG_TEGRA_HOST1X) += host1x.o diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c index 64d9b707ee1a..314bf3718cc7 100644 --- a/drivers/gpu/host1x/dev.c +++ b/drivers/gpu/host1x/dev.c @@ -35,6 +35,7 @@ #include "hw/host1x01.h" #include "hw/host1x02.h" #include "hw/host1x04.h" +#include "hw/host1x05.h" void host1x_sync_writel(struct host1x *host1x, u32 v, u32 r) { @@ -87,7 +88,17 @@ static const struct host1x_info host1x04_info = { .sync_offset = 0x2100, }; +static const struct host1x_info host1x05_info = { + .nb_channels = 14, + .nb_pts = 192, + .nb_mlocks = 16, + .nb_bases = 64, + .init = host1x05_init, + .sync_offset = 0x2100, +}; + static struct of_device_id host1x_of_match[] = { + { .compatible = "nvidia,tegra210-host1x", .data = &host1x05_info, }, { .compatible = "nvidia,tegra124-host1x", .data = &host1x04_info, }, { .compatible = "nvidia,tegra114-host1x", .data = &host1x02_info, }, { .compatible = "nvidia,tegra30-host1x", .data = &host1x01_info, }, diff --git a/drivers/gpu/host1x/hw/host1x05.c b/drivers/gpu/host1x/hw/host1x05.c new file mode 100644 index 000000000000..047097ce3bad --- /dev/null +++ b/drivers/gpu/host1x/hw/host1x05.c @@ -0,0 +1,42 @@ +/* + * Host1x init for Tegra210 SoCs + * + * Copyright (c) 2015 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* include hw specification */ +#include "host1x05.h" +#include "host1x05_hardware.h" + +/* include code */ +#include "cdma_hw.c" +#include "channel_hw.c" +#include "debug_hw.c" +#include "intr_hw.c" +#include "syncpt_hw.c" + +#include "../dev.h" + +int host1x05_init(struct host1x *host) +{ + host->channel_op = &host1x_channel_ops; + host->cdma_op = &host1x_cdma_ops; + host->cdma_pb_op = &host1x_pushbuffer_ops; + host->syncpt_op = &host1x_syncpt_ops; + host->intr_op = &host1x_intr_ops; + host->debug_op = &host1x_debug_ops; + + return 0; +} diff --git a/drivers/gpu/host1x/hw/host1x05.h b/drivers/gpu/host1x/hw/host1x05.h new file mode 100644 index 000000000000..a306d9c05cd5 --- /dev/null +++ b/drivers/gpu/host1x/hw/host1x05.h @@ -0,0 +1,26 @@ +/* + * Host1x init for Tegra210 SoCs + * + * Copyright (c) 2015 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef HOST1X_HOST1X05_H +#define HOST1X_HOST1X05_H + +struct host1x; + +int host1x05_init(struct host1x *host); + +#endif diff --git a/drivers/gpu/host1x/hw/host1x05_hardware.h b/drivers/gpu/host1x/hw/host1x05_hardware.h new file mode 100644 index 000000000000..2937ebb6be11 --- /dev/null +++ b/drivers/gpu/host1x/hw/host1x05_hardware.h @@ -0,0 +1,142 @@ +/* + * Tegra host1x Register Offsets for Tegra210 + * + * Copyright (c) 2015 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __HOST1X_HOST1X05_HARDWARE_H +#define __HOST1X_HOST1X05_HARDWARE_H + +#include +#include + +#include "hw_host1x05_channel.h" +#include "hw_host1x05_sync.h" +#include "hw_host1x05_uclass.h" + +static inline u32 host1x_class_host_wait_syncpt( + unsigned indx, unsigned threshold) +{ + return host1x_uclass_wait_syncpt_indx_f(indx) + | host1x_uclass_wait_syncpt_thresh_f(threshold); +} + +static inline u32 host1x_class_host_load_syncpt_base( + unsigned indx, unsigned threshold) +{ + return host1x_uclass_load_syncpt_base_base_indx_f(indx) + | host1x_uclass_load_syncpt_base_value_f(threshold); +} + +static inline u32 host1x_class_host_wait_syncpt_base( + unsigned indx, unsigned base_indx, unsigned offset) +{ + return host1x_uclass_wait_syncpt_base_indx_f(indx) + | host1x_uclass_wait_syncpt_base_base_indx_f(base_indx) + | host1x_uclass_wait_syncpt_base_offset_f(offset); +} + +static inline u32 host1x_class_host_incr_syncpt_base( + unsigned base_indx, unsigned offset) +{ + return host1x_uclass_incr_syncpt_base_base_indx_f(base_indx) + | host1x_uclass_incr_syncpt_base_offset_f(offset); +} + +static inline u32 host1x_class_host_incr_syncpt( + unsigned cond, unsigned indx) +{ + return host1x_uclass_incr_syncpt_cond_f(cond) + | host1x_uclass_incr_syncpt_indx_f(indx); +} + +static inline u32 host1x_class_host_indoff_reg_write( + unsigned mod_id, unsigned offset, bool auto_inc) +{ + u32 v = host1x_uclass_indoff_indbe_f(0xf) + | host1x_uclass_indoff_indmodid_f(mod_id) + | host1x_uclass_indoff_indroffset_f(offset); + if (auto_inc) + v |= host1x_uclass_indoff_autoinc_f(1); + return v; +} + +static inline u32 host1x_class_host_indoff_reg_read( + unsigned mod_id, unsigned offset, bool auto_inc) +{ + u32 v = host1x_uclass_indoff_indmodid_f(mod_id) + | host1x_uclass_indoff_indroffset_f(offset) + | host1x_uclass_indoff_rwn_read_v(); + if (auto_inc) + v |= host1x_uclass_indoff_autoinc_f(1); + return v; +} + +/* cdma opcodes */ +static inline u32 host1x_opcode_setclass( + unsigned class_id, unsigned offset, unsigned mask) +{ + return (0 << 28) | (offset << 16) | (class_id << 6) | mask; +} + +static inline u32 host1x_opcode_incr(unsigned offset, unsigned count) +{ + return (1 << 28) | (offset << 16) | count; +} + +static inline u32 host1x_opcode_nonincr(unsigned offset, unsigned count) +{ + return (2 << 28) | (offset << 16) | count; +} + +static inline u32 host1x_opcode_mask(unsigned offset, unsigned mask) +{ + return (3 << 28) | (offset << 16) | mask; +} + +static inline u32 host1x_opcode_imm(unsigned offset, unsigned value) +{ + return (4 << 28) | (offset << 16) | value; +} + +static inline u32 host1x_opcode_imm_incr_syncpt(unsigned cond, unsigned indx) +{ + return host1x_opcode_imm(host1x_uclass_incr_syncpt_r(), + host1x_class_host_incr_syncpt(cond, indx)); +} + +static inline u32 host1x_opcode_restart(unsigned address) +{ + return (5 << 28) | (address >> 4); +} + +static inline u32 host1x_opcode_gather(unsigned count) +{ + return (6 << 28) | count; +} + +static inline u32 host1x_opcode_gather_nonincr(unsigned offset, unsigned count) +{ + return (6 << 28) | (offset << 16) | BIT(15) | count; +} + +static inline u32 host1x_opcode_gather_incr(unsigned offset, unsigned count) +{ + return (6 << 28) | (offset << 16) | BIT(15) | BIT(14) | count; +} + +#define HOST1X_OPCODE_NOP host1x_opcode_nonincr(0, 0) + +#endif diff --git a/drivers/gpu/host1x/hw/hw_host1x05_channel.h b/drivers/gpu/host1x/hw/hw_host1x05_channel.h new file mode 100644 index 000000000000..fce6e2c1ff4c --- /dev/null +++ b/drivers/gpu/host1x/hw/hw_host1x05_channel.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2015 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + /* + * Function naming determines intended use: + * + * _r(void) : Returns the offset for register . + * + * _w(void) : Returns the word offset for word (4 byte) element . + * + * __s(void) : Returns size of field of register in bits. + * + * __f(u32 v) : Returns a value based on 'v' which has been shifted + * and masked to place it at field of register . This value + * can be |'d with others to produce a full register value for + * register . + * + * __m(void) : Returns a mask for field of register . This + * value can be ~'d and then &'d to clear the value of field for + * register . + * + * ___f(void) : Returns the constant value after being shifted + * to place it at field of register . This value can be |'d + * with others to produce a full register value for . + * + * __v(u32 r) : Returns the value of field from a full register + * value 'r' after being shifted to place its LSB at bit 0. + * This value is suitable for direct comparison with other unshifted + * values appropriate for use in field of register . + * + * ___v(void) : Returns the constant value for defined for + * field of register . This value is suitable for direct + * comparison with unshifted values appropriate for use in field + * of register . + */ + +#ifndef HOST1X_HW_HOST1X05_CHANNEL_H +#define HOST1X_HW_HOST1X05_CHANNEL_H + +static inline u32 host1x_channel_fifostat_r(void) +{ + return 0x0; +} +#define HOST1X_CHANNEL_FIFOSTAT \ + host1x_channel_fifostat_r() +static inline u32 host1x_channel_fifostat_cfempty_v(u32 r) +{ + return (r >> 11) & 0x1; +} +#define HOST1X_CHANNEL_FIFOSTAT_CFEMPTY_V(r) \ + host1x_channel_fifostat_cfempty_v(r) +static inline u32 host1x_channel_dmastart_r(void) +{ + return 0x14; +} +#define HOST1X_CHANNEL_DMASTART \ + host1x_channel_dmastart_r() +static inline u32 host1x_channel_dmaput_r(void) +{ + return 0x18; +} +#define HOST1X_CHANNEL_DMAPUT \ + host1x_channel_dmaput_r() +static inline u32 host1x_channel_dmaget_r(void) +{ + return 0x1c; +} +#define HOST1X_CHANNEL_DMAGET \ + host1x_channel_dmaget_r() +static inline u32 host1x_channel_dmaend_r(void) +{ + return 0x20; +} +#define HOST1X_CHANNEL_DMAEND \ + host1x_channel_dmaend_r() +static inline u32 host1x_channel_dmactrl_r(void) +{ + return 0x24; +} +#define HOST1X_CHANNEL_DMACTRL \ + host1x_channel_dmactrl_r() +static inline u32 host1x_channel_dmactrl_dmastop(void) +{ + return 1 << 0; +} +#define HOST1X_CHANNEL_DMACTRL_DMASTOP \ + host1x_channel_dmactrl_dmastop() +static inline u32 host1x_channel_dmactrl_dmastop_v(u32 r) +{ + return (r >> 0) & 0x1; +} +#define HOST1X_CHANNEL_DMACTRL_DMASTOP_V(r) \ + host1x_channel_dmactrl_dmastop_v(r) +static inline u32 host1x_channel_dmactrl_dmagetrst(void) +{ + return 1 << 1; +} +#define HOST1X_CHANNEL_DMACTRL_DMAGETRST \ + host1x_channel_dmactrl_dmagetrst() +static inline u32 host1x_channel_dmactrl_dmainitget(void) +{ + return 1 << 2; +} +#define HOST1X_CHANNEL_DMACTRL_DMAINITGET \ + host1x_channel_dmactrl_dmainitget() + +#endif diff --git a/drivers/gpu/host1x/hw/hw_host1x05_sync.h b/drivers/gpu/host1x/hw/hw_host1x05_sync.h new file mode 100644 index 000000000000..ca10eee5045c --- /dev/null +++ b/drivers/gpu/host1x/hw/hw_host1x05_sync.h @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2015 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + /* + * Function naming determines intended use: + * + * _r(void) : Returns the offset for register . + * + * _w(void) : Returns the word offset for word (4 byte) element . + * + * __s(void) : Returns size of field of register in bits. + * + * __f(u32 v) : Returns a value based on 'v' which has been shifted + * and masked to place it at field of register . This value + * can be |'d with others to produce a full register value for + * register . + * + * __m(void) : Returns a mask for field of register . This + * value can be ~'d and then &'d to clear the value of field for + * register . + * + * ___f(void) : Returns the constant value after being shifted + * to place it at field of register . This value can be |'d + * with others to produce a full register value for . + * + * __v(u32 r) : Returns the value of field from a full register + * value 'r' after being shifted to place its LSB at bit 0. + * This value is suitable for direct comparison with other unshifted + * values appropriate for use in field of register . + * + * ___v(void) : Returns the constant value for defined for + * field of register . This value is suitable for direct + * comparison with unshifted values appropriate for use in field + * of register . + */ + +#ifndef HOST1X_HW_HOST1X05_SYNC_H +#define HOST1X_HW_HOST1X05_SYNC_H + +#define REGISTER_STRIDE 4 + +static inline u32 host1x_sync_syncpt_r(unsigned int id) +{ + return 0xf80 + id * REGISTER_STRIDE; +} +#define HOST1X_SYNC_SYNCPT(id) \ + host1x_sync_syncpt_r(id) +static inline u32 host1x_sync_syncpt_thresh_cpu0_int_status_r(unsigned int id) +{ + return 0xe80 + id * REGISTER_STRIDE; +} +#define HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(id) \ + host1x_sync_syncpt_thresh_cpu0_int_status_r(id) +static inline u32 host1x_sync_syncpt_thresh_int_disable_r(unsigned int id) +{ + return 0xf00 + id * REGISTER_STRIDE; +} +#define HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(id) \ + host1x_sync_syncpt_thresh_int_disable_r(id) +static inline u32 host1x_sync_syncpt_thresh_int_enable_cpu0_r(unsigned int id) +{ + return 0xf20 + id * REGISTER_STRIDE; +} +#define HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0(id) \ + host1x_sync_syncpt_thresh_int_enable_cpu0_r(id) +static inline u32 host1x_sync_cf_setup_r(unsigned int channel) +{ + return 0xc00 + channel * REGISTER_STRIDE; +} +#define HOST1X_SYNC_CF_SETUP(channel) \ + host1x_sync_cf_setup_r(channel) +static inline u32 host1x_sync_cf_setup_base_v(u32 r) +{ + return (r >> 0) & 0x3ff; +} +#define HOST1X_SYNC_CF_SETUP_BASE_V(r) \ + host1x_sync_cf_setup_base_v(r) +static inline u32 host1x_sync_cf_setup_limit_v(u32 r) +{ + return (r >> 16) & 0x3ff; +} +#define HOST1X_SYNC_CF_SETUP_LIMIT_V(r) \ + host1x_sync_cf_setup_limit_v(r) +static inline u32 host1x_sync_cmdproc_stop_r(void) +{ + return 0xac; +} +#define HOST1X_SYNC_CMDPROC_STOP \ + host1x_sync_cmdproc_stop_r() +static inline u32 host1x_sync_ch_teardown_r(void) +{ + return 0xb0; +} +#define HOST1X_SYNC_CH_TEARDOWN \ + host1x_sync_ch_teardown_r() +static inline u32 host1x_sync_usec_clk_r(void) +{ + return 0x1a4; +} +#define HOST1X_SYNC_USEC_CLK \ + host1x_sync_usec_clk_r() +static inline u32 host1x_sync_ctxsw_timeout_cfg_r(void) +{ + return 0x1a8; +} +#define HOST1X_SYNC_CTXSW_TIMEOUT_CFG \ + host1x_sync_ctxsw_timeout_cfg_r() +static inline u32 host1x_sync_ip_busy_timeout_r(void) +{ + return 0x1bc; +} +#define HOST1X_SYNC_IP_BUSY_TIMEOUT \ + host1x_sync_ip_busy_timeout_r() +static inline u32 host1x_sync_mlock_owner_r(unsigned int id) +{ + return 0x340 + id * REGISTER_STRIDE; +} +#define HOST1X_SYNC_MLOCK_OWNER(id) \ + host1x_sync_mlock_owner_r(id) +static inline u32 host1x_sync_mlock_owner_chid_v(u32 r) +{ + return (r >> 8) & 0xf; +} +#define HOST1X_SYNC_MLOCK_OWNER_CHID_V(v) \ + host1x_sync_mlock_owner_chid_v(v) +static inline u32 host1x_sync_mlock_owner_cpu_owns_v(u32 r) +{ + return (r >> 1) & 0x1; +} +#define HOST1X_SYNC_MLOCK_OWNER_CPU_OWNS_V(r) \ + host1x_sync_mlock_owner_cpu_owns_v(r) +static inline u32 host1x_sync_mlock_owner_ch_owns_v(u32 r) +{ + return (r >> 0) & 0x1; +} +#define HOST1X_SYNC_MLOCK_OWNER_CH_OWNS_V(r) \ + host1x_sync_mlock_owner_ch_owns_v(r) +static inline u32 host1x_sync_syncpt_int_thresh_r(unsigned int id) +{ + return 0x1380 + id * REGISTER_STRIDE; +} +#define HOST1X_SYNC_SYNCPT_INT_THRESH(id) \ + host1x_sync_syncpt_int_thresh_r(id) +static inline u32 host1x_sync_syncpt_base_r(unsigned int id) +{ + return 0x600 + id * REGISTER_STRIDE; +} +#define HOST1X_SYNC_SYNCPT_BASE(id) \ + host1x_sync_syncpt_base_r(id) +static inline u32 host1x_sync_syncpt_cpu_incr_r(unsigned int id) +{ + return 0xf60 + id * REGISTER_STRIDE; +} +#define HOST1X_SYNC_SYNCPT_CPU_INCR(id) \ + host1x_sync_syncpt_cpu_incr_r(id) +static inline u32 host1x_sync_cbread_r(unsigned int channel) +{ + return 0xc80 + channel * REGISTER_STRIDE; +} +#define HOST1X_SYNC_CBREAD(channel) \ + host1x_sync_cbread_r(channel) +static inline u32 host1x_sync_cfpeek_ctrl_r(void) +{ + return 0x74c; +} +#define HOST1X_SYNC_CFPEEK_CTRL \ + host1x_sync_cfpeek_ctrl_r() +static inline u32 host1x_sync_cfpeek_ctrl_addr_f(u32 v) +{ + return (v & 0x3ff) << 0; +} +#define HOST1X_SYNC_CFPEEK_CTRL_ADDR_F(v) \ + host1x_sync_cfpeek_ctrl_addr_f(v) +static inline u32 host1x_sync_cfpeek_ctrl_channr_f(u32 v) +{ + return (v & 0xf) << 16; +} +#define HOST1X_SYNC_CFPEEK_CTRL_CHANNR_F(v) \ + host1x_sync_cfpeek_ctrl_channr_f(v) +static inline u32 host1x_sync_cfpeek_ctrl_ena_f(u32 v) +{ + return (v & 0x1) << 31; +} +#define HOST1X_SYNC_CFPEEK_CTRL_ENA_F(v) \ + host1x_sync_cfpeek_ctrl_ena_f(v) +static inline u32 host1x_sync_cfpeek_read_r(void) +{ + return 0x750; +} +#define HOST1X_SYNC_CFPEEK_READ \ + host1x_sync_cfpeek_read_r() +static inline u32 host1x_sync_cfpeek_ptrs_r(void) +{ + return 0x754; +} +#define HOST1X_SYNC_CFPEEK_PTRS \ + host1x_sync_cfpeek_ptrs_r() +static inline u32 host1x_sync_cfpeek_ptrs_cf_rd_ptr_v(u32 r) +{ + return (r >> 0) & 0x3ff; +} +#define HOST1X_SYNC_CFPEEK_PTRS_CF_RD_PTR_V(r) \ + host1x_sync_cfpeek_ptrs_cf_rd_ptr_v(r) +static inline u32 host1x_sync_cfpeek_ptrs_cf_wr_ptr_v(u32 r) +{ + return (r >> 16) & 0x3ff; +} +#define HOST1X_SYNC_CFPEEK_PTRS_CF_WR_PTR_V(r) \ + host1x_sync_cfpeek_ptrs_cf_wr_ptr_v(r) +static inline u32 host1x_sync_cbstat_r(unsigned int channel) +{ + return 0xcc0 + channel * REGISTER_STRIDE; +} +#define HOST1X_SYNC_CBSTAT(channel) \ + host1x_sync_cbstat_r(channel) +static inline u32 host1x_sync_cbstat_cboffset_v(u32 r) +{ + return (r >> 0) & 0xffff; +} +#define HOST1X_SYNC_CBSTAT_CBOFFSET_V(r) \ + host1x_sync_cbstat_cboffset_v(r) +static inline u32 host1x_sync_cbstat_cbclass_v(u32 r) +{ + return (r >> 16) & 0x3ff; +} +#define HOST1X_SYNC_CBSTAT_CBCLASS_V(r) \ + host1x_sync_cbstat_cbclass_v(r) + +#endif diff --git a/drivers/gpu/host1x/hw/hw_host1x05_uclass.h b/drivers/gpu/host1x/hw/hw_host1x05_uclass.h new file mode 100644 index 000000000000..0c411da6bc41 --- /dev/null +++ b/drivers/gpu/host1x/hw/hw_host1x05_uclass.h @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2015 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + /* + * Function naming determines intended use: + * + * _r(void) : Returns the offset for register . + * + * _w(void) : Returns the word offset for word (4 byte) element . + * + * __s(void) : Returns size of field of register in bits. + * + * __f(u32 v) : Returns a value based on 'v' which has been shifted + * and masked to place it at field of register . This value + * can be |'d with others to produce a full register value for + * register . + * + * __m(void) : Returns a mask for field of register . This + * value can be ~'d and then &'d to clear the value of field for + * register . + * + * ___f(void) : Returns the constant value after being shifted + * to place it at field of register . This value can be |'d + * with others to produce a full register value for . + * + * __v(u32 r) : Returns the value of field from a full register + * value 'r' after being shifted to place its LSB at bit 0. + * This value is suitable for direct comparison with other unshifted + * values appropriate for use in field of register . + * + * ___v(void) : Returns the constant value for defined for + * field of register . This value is suitable for direct + * comparison with unshifted values appropriate for use in field + * of register . + */ + +#ifndef HOST1X_HW_HOST1X05_UCLASS_H +#define HOST1X_HW_HOST1X05_UCLASS_H + +static inline u32 host1x_uclass_incr_syncpt_r(void) +{ + return 0x0; +} +#define HOST1X_UCLASS_INCR_SYNCPT \ + host1x_uclass_incr_syncpt_r() +static inline u32 host1x_uclass_incr_syncpt_cond_f(u32 v) +{ + return (v & 0xff) << 8; +} +#define HOST1X_UCLASS_INCR_SYNCPT_COND_F(v) \ + host1x_uclass_incr_syncpt_cond_f(v) +static inline u32 host1x_uclass_incr_syncpt_indx_f(u32 v) +{ + return (v & 0xff) << 0; +} +#define HOST1X_UCLASS_INCR_SYNCPT_INDX_F(v) \ + host1x_uclass_incr_syncpt_indx_f(v) +static inline u32 host1x_uclass_wait_syncpt_r(void) +{ + return 0x8; +} +#define HOST1X_UCLASS_WAIT_SYNCPT \ + host1x_uclass_wait_syncpt_r() +static inline u32 host1x_uclass_wait_syncpt_indx_f(u32 v) +{ + return (v & 0xff) << 24; +} +#define HOST1X_UCLASS_WAIT_SYNCPT_INDX_F(v) \ + host1x_uclass_wait_syncpt_indx_f(v) +static inline u32 host1x_uclass_wait_syncpt_thresh_f(u32 v) +{ + return (v & 0xffffff) << 0; +} +#define HOST1X_UCLASS_WAIT_SYNCPT_THRESH_F(v) \ + host1x_uclass_wait_syncpt_thresh_f(v) +static inline u32 host1x_uclass_wait_syncpt_base_r(void) +{ + return 0x9; +} +#define HOST1X_UCLASS_WAIT_SYNCPT_BASE \ + host1x_uclass_wait_syncpt_base_r() +static inline u32 host1x_uclass_wait_syncpt_base_indx_f(u32 v) +{ + return (v & 0xff) << 24; +} +#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_INDX_F(v) \ + host1x_uclass_wait_syncpt_base_indx_f(v) +static inline u32 host1x_uclass_wait_syncpt_base_base_indx_f(u32 v) +{ + return (v & 0xff) << 16; +} +#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_BASE_INDX_F(v) \ + host1x_uclass_wait_syncpt_base_base_indx_f(v) +static inline u32 host1x_uclass_wait_syncpt_base_offset_f(u32 v) +{ + return (v & 0xffff) << 0; +} +#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_OFFSET_F(v) \ + host1x_uclass_wait_syncpt_base_offset_f(v) +static inline u32 host1x_uclass_load_syncpt_base_r(void) +{ + return 0xb; +} +#define HOST1X_UCLASS_LOAD_SYNCPT_BASE \ + host1x_uclass_load_syncpt_base_r() +static inline u32 host1x_uclass_load_syncpt_base_base_indx_f(u32 v) +{ + return (v & 0xff) << 24; +} +#define HOST1X_UCLASS_LOAD_SYNCPT_BASE_BASE_INDX_F(v) \ + host1x_uclass_load_syncpt_base_base_indx_f(v) +static inline u32 host1x_uclass_load_syncpt_base_value_f(u32 v) +{ + return (v & 0xffffff) << 0; +} +#define HOST1X_UCLASS_LOAD_SYNCPT_BASE_VALUE_F(v) \ + host1x_uclass_load_syncpt_base_value_f(v) +static inline u32 host1x_uclass_incr_syncpt_base_base_indx_f(u32 v) +{ + return (v & 0xff) << 24; +} +#define HOST1X_UCLASS_INCR_SYNCPT_BASE_BASE_INDX_F(v) \ + host1x_uclass_incr_syncpt_base_base_indx_f(v) +static inline u32 host1x_uclass_incr_syncpt_base_offset_f(u32 v) +{ + return (v & 0xffffff) << 0; +} +#define HOST1X_UCLASS_INCR_SYNCPT_BASE_OFFSET_F(v) \ + host1x_uclass_incr_syncpt_base_offset_f(v) +static inline u32 host1x_uclass_indoff_r(void) +{ + return 0x2d; +} +#define HOST1X_UCLASS_INDOFF \ + host1x_uclass_indoff_r() +static inline u32 host1x_uclass_indoff_indbe_f(u32 v) +{ + return (v & 0xf) << 28; +} +#define HOST1X_UCLASS_INDOFF_INDBE_F(v) \ + host1x_uclass_indoff_indbe_f(v) +static inline u32 host1x_uclass_indoff_autoinc_f(u32 v) +{ + return (v & 0x1) << 27; +} +#define HOST1X_UCLASS_INDOFF_AUTOINC_F(v) \ + host1x_uclass_indoff_autoinc_f(v) +static inline u32 host1x_uclass_indoff_indmodid_f(u32 v) +{ + return (v & 0xff) << 18; +} +#define HOST1X_UCLASS_INDOFF_INDMODID_F(v) \ + host1x_uclass_indoff_indmodid_f(v) +static inline u32 host1x_uclass_indoff_indroffset_f(u32 v) +{ + return (v & 0xffff) << 2; +} +#define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \ + host1x_uclass_indoff_indroffset_f(v) +static inline u32 host1x_uclass_indoff_rwn_read_v(void) +{ + return 1; +} +#define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \ + host1x_uclass_indoff_indroffset_f(v) + +#endif From 473112e443c0ff47bc21dc6efac1fabae8613ad1 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 10 Sep 2015 16:07:14 +0200 Subject: [PATCH 04/14] drm/tegra: Use new multi-driver module helpers Use the new multi-driver module helpers to get rid of some boilerplate in the module initialization and cleanup functions. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/drm.c | 56 ++++++++----------------------------- drivers/gpu/drm/tegra/drm.h | 4 +-- 2 files changed, 14 insertions(+), 46 deletions(-) diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index e0f827790a5e..dc8e7b8db54b 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -1076,6 +1076,16 @@ static struct host1x_driver host1x_drm_driver = { .subdevs = host1x_drm_subdevs, }; +static struct platform_driver * const drivers[] = { + &tegra_dc_driver, + &tegra_hdmi_driver, + &tegra_dsi_driver, + &tegra_dpaux_driver, + &tegra_sor_driver, + &tegra_gr2d_driver, + &tegra_gr3d_driver, +}; + static int __init host1x_drm_init(void) { int err; @@ -1084,48 +1094,12 @@ static int __init host1x_drm_init(void) if (err < 0) return err; - err = platform_driver_register(&tegra_dc_driver); + err = platform_register_drivers(drivers, ARRAY_SIZE(drivers)); if (err < 0) goto unregister_host1x; - err = platform_driver_register(&tegra_dsi_driver); - if (err < 0) - goto unregister_dc; - - err = platform_driver_register(&tegra_sor_driver); - if (err < 0) - goto unregister_dsi; - - err = platform_driver_register(&tegra_hdmi_driver); - if (err < 0) - goto unregister_sor; - - err = platform_driver_register(&tegra_dpaux_driver); - if (err < 0) - goto unregister_hdmi; - - err = platform_driver_register(&tegra_gr2d_driver); - if (err < 0) - goto unregister_dpaux; - - err = platform_driver_register(&tegra_gr3d_driver); - if (err < 0) - goto unregister_gr2d; - return 0; -unregister_gr2d: - platform_driver_unregister(&tegra_gr2d_driver); -unregister_dpaux: - platform_driver_unregister(&tegra_dpaux_driver); -unregister_hdmi: - platform_driver_unregister(&tegra_hdmi_driver); -unregister_sor: - platform_driver_unregister(&tegra_sor_driver); -unregister_dsi: - platform_driver_unregister(&tegra_dsi_driver); -unregister_dc: - platform_driver_unregister(&tegra_dc_driver); unregister_host1x: host1x_driver_unregister(&host1x_drm_driver); return err; @@ -1134,13 +1108,7 @@ module_init(host1x_drm_init); static void __exit host1x_drm_exit(void) { - platform_driver_unregister(&tegra_gr3d_driver); - platform_driver_unregister(&tegra_gr2d_driver); - platform_driver_unregister(&tegra_dpaux_driver); - platform_driver_unregister(&tegra_hdmi_driver); - platform_driver_unregister(&tegra_sor_driver); - platform_driver_unregister(&tegra_dsi_driver); - platform_driver_unregister(&tegra_dc_driver); + platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); host1x_driver_unregister(&host1x_drm_driver); } module_exit(host1x_drm_exit); diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index d88a2d18c1a4..a9728146d846 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -279,10 +279,10 @@ void tegra_fb_output_poll_changed(struct drm_device *drm); #endif extern struct platform_driver tegra_dc_driver; -extern struct platform_driver tegra_dsi_driver; -extern struct platform_driver tegra_sor_driver; extern struct platform_driver tegra_hdmi_driver; +extern struct platform_driver tegra_dsi_driver; extern struct platform_driver tegra_dpaux_driver; +extern struct platform_driver tegra_sor_driver; extern struct platform_driver tegra_gr2d_driver; extern struct platform_driver tegra_gr3d_driver; From 115333042c73958cc7c2c76cddd1dfe5b1db1b7f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 23 Nov 2015 10:32:40 +0100 Subject: [PATCH 05/14] drm/tegra: Use unlocked gem unreferencing For drm_gem_object_unreference callers are required to hold dev->struct_mutex, which these paths don't. Enforcing this requirement has become a bit more strict with commit ef4c6270bf2867e2f8032e9614d1a8cfc6c71663 Author: Daniel Vetter Date: Thu Oct 15 09:36:25 2015 +0200 drm/gem: Check locking in drm_gem_object_unreference Cc: Thierry Reding Signed-off-by: Daniel Vetter Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/drm.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index dc8e7b8db54b..b26f62e7334d 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -473,7 +473,7 @@ static int tegra_gem_mmap(struct drm_device *drm, void *data, args->offset = drm_vma_node_offset_addr(&bo->gem.vma_node); - drm_gem_object_unreference(gem); + drm_gem_object_unreference_unlocked(gem); return 0; } @@ -683,7 +683,7 @@ static int tegra_gem_set_tiling(struct drm_device *drm, void *data, bo->tiling.mode = mode; bo->tiling.value = value; - drm_gem_object_unreference(gem); + drm_gem_object_unreference_unlocked(gem); return 0; } @@ -723,7 +723,7 @@ static int tegra_gem_get_tiling(struct drm_device *drm, void *data, break; } - drm_gem_object_unreference(gem); + drm_gem_object_unreference_unlocked(gem); return err; } @@ -748,7 +748,7 @@ static int tegra_gem_set_flags(struct drm_device *drm, void *data, if (args->flags & DRM_TEGRA_GEM_BOTTOM_UP) bo->flags |= TEGRA_BO_BOTTOM_UP; - drm_gem_object_unreference(gem); + drm_gem_object_unreference_unlocked(gem); return 0; } @@ -770,7 +770,7 @@ static int tegra_gem_get_flags(struct drm_device *drm, void *data, if (bo->flags & TEGRA_BO_BOTTOM_UP) args->flags |= DRM_TEGRA_GEM_BOTTOM_UP; - drm_gem_object_unreference(gem); + drm_gem_object_unreference_unlocked(gem); return 0; } From d849c82fac61921301cf2d6f0a3477a121a2ef2a Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 23 Nov 2015 10:32:47 +0100 Subject: [PATCH 06/14] drm/tegra: Don't take dev->struct_mutex in mmap offset ioctl Since David Herrmann's mmap vma manager rework we don't need to grab dev->struct_mutex any more to prevent races when looking up the mmap offset. Drop it and instead don't forget to use the unref_unlocked variant (since the drm core still cares). v2: Finally get rid of the copypasta from another commit in this commit message. And convert to _unlocked like we need to (Patrik). Cc: Patrik Jakobsson Cc: Thierry Reding Acked-by: Thierry Reding Reviewed-by: Patrik Jakobsson Signed-off-by: Daniel Vetter Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/gem.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index 01e16e146bfe..fb712316c522 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -408,12 +408,9 @@ int tegra_bo_dumb_map_offset(struct drm_file *file, struct drm_device *drm, struct drm_gem_object *gem; struct tegra_bo *bo; - mutex_lock(&drm->struct_mutex); - gem = drm_gem_object_lookup(drm, file, handle); if (!gem) { dev_err(drm->dev, "failed to lookup GEM object\n"); - mutex_unlock(&drm->struct_mutex); return -EINVAL; } @@ -421,9 +418,7 @@ int tegra_bo_dumb_map_offset(struct drm_file *file, struct drm_device *drm, *offset = drm_vma_node_offset_addr(&bo->gem.vma_node); - drm_gem_object_unreference(gem); - - mutex_unlock(&drm->struct_mutex); + drm_gem_object_unreference_unlocked(gem); return 0; } From a07cdfe5389e0ca43f525c7d1da0930b7447e0c8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 23 Nov 2015 10:32:48 +0100 Subject: [PATCH 07/14] drm/tegra: Use drm_gem_object_unreference_unlocked() This only grabs the mutex when really needed, but still has a might- acquire lockdep check to make sure that's always possible. With this patch Tegra DRM is officially struct_mutex free, yay! v2: refernce_unlocked doesn't exist as kbuild spotted. Cc: Thierry Reding Acked-by: Thierry Reding Signed-off-by: Daniel Vetter [treding@nvidia.com: remove unused variables] Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/drm.c | 4 +--- drivers/gpu/drm/tegra/gem.c | 8 +------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index b26f62e7334d..c4910d528411 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -277,9 +277,7 @@ host1x_bo_lookup(struct drm_device *drm, struct drm_file *file, u32 handle) if (!gem) return NULL; - mutex_lock(&drm->struct_mutex); - drm_gem_object_unreference(gem); - mutex_unlock(&drm->struct_mutex); + drm_gem_object_unreference_unlocked(gem); bo = to_tegra_bo(gem); return &bo->base; diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index fb712316c522..33add93b4ed9 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -28,11 +28,8 @@ static inline struct tegra_bo *host1x_to_tegra_bo(struct host1x_bo *bo) static void tegra_bo_put(struct host1x_bo *bo) { struct tegra_bo *obj = host1x_to_tegra_bo(bo); - struct drm_device *drm = obj->gem.dev; - mutex_lock(&drm->struct_mutex); - drm_gem_object_unreference(&obj->gem); - mutex_unlock(&drm->struct_mutex); + drm_gem_object_unreference_unlocked(&obj->gem); } static dma_addr_t tegra_bo_pin(struct host1x_bo *bo, struct sg_table **sgt) @@ -72,11 +69,8 @@ static void tegra_bo_kunmap(struct host1x_bo *bo, unsigned int page, static struct host1x_bo *tegra_bo_get(struct host1x_bo *bo) { struct tegra_bo *obj = host1x_to_tegra_bo(bo); - struct drm_device *drm = obj->gem.dev; - mutex_lock(&drm->struct_mutex); drm_gem_object_reference(&obj->gem); - mutex_unlock(&drm->struct_mutex); return bo; } From 9542c2376efaf5a6ae65b4aa0014e9f35ba36e17 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 8 Jul 2015 13:39:09 +0200 Subject: [PATCH 08/14] drm/tegra: sor: Operate on struct drm_dp_aux * Instead of getting a pointer to the driver-specific wrapper of AUX channels, use the AUX channel objects directly to avoid hackish casting between the two types. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dpaux.c | 41 +++++++++++++----------- drivers/gpu/drm/tegra/drm.h | 19 ++++++----- drivers/gpu/drm/tegra/sor.c | 60 ++++++++++++++++------------------- 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/drivers/gpu/drm/tegra/dpaux.c b/drivers/gpu/drm/tegra/dpaux.c index 6aecb6647313..b24a0f14821a 100644 --- a/drivers/gpu/drm/tegra/dpaux.c +++ b/drivers/gpu/drm/tegra/dpaux.c @@ -436,7 +436,7 @@ struct platform_driver tegra_dpaux_driver = { .remove = tegra_dpaux_remove, }; -struct tegra_dpaux *tegra_dpaux_find_by_of_node(struct device_node *np) +struct drm_dp_aux *drm_dp_aux_find_by_of_node(struct device_node *np) { struct tegra_dpaux *dpaux; @@ -445,7 +445,7 @@ struct tegra_dpaux *tegra_dpaux_find_by_of_node(struct device_node *np) list_for_each_entry(dpaux, &dpaux_list, list) if (np == dpaux->dev->of_node) { mutex_unlock(&dpaux_lock); - return dpaux; + return &dpaux->aux; } mutex_unlock(&dpaux_lock); @@ -453,8 +453,9 @@ struct tegra_dpaux *tegra_dpaux_find_by_of_node(struct device_node *np) return NULL; } -int tegra_dpaux_attach(struct tegra_dpaux *dpaux, struct tegra_output *output) +int drm_dp_aux_attach(struct drm_dp_aux *aux, struct tegra_output *output) { + struct tegra_dpaux *dpaux = to_dpaux(aux); unsigned long timeout; int err; @@ -470,7 +471,7 @@ int tegra_dpaux_attach(struct tegra_dpaux *dpaux, struct tegra_output *output) while (time_before(jiffies, timeout)) { enum drm_connector_status status; - status = tegra_dpaux_detect(dpaux); + status = drm_dp_aux_detect(aux); if (status == connector_status_connected) { enable_irq(dpaux->irq); return 0; @@ -482,8 +483,9 @@ int tegra_dpaux_attach(struct tegra_dpaux *dpaux, struct tegra_output *output) return -ETIMEDOUT; } -int tegra_dpaux_detach(struct tegra_dpaux *dpaux) +int drm_dp_aux_detach(struct drm_dp_aux *aux) { + struct tegra_dpaux *dpaux = to_dpaux(aux); unsigned long timeout; int err; @@ -498,7 +500,7 @@ int tegra_dpaux_detach(struct tegra_dpaux *dpaux) while (time_before(jiffies, timeout)) { enum drm_connector_status status; - status = tegra_dpaux_detect(dpaux); + status = drm_dp_aux_detect(aux); if (status == connector_status_disconnected) { dpaux->output = NULL; return 0; @@ -510,8 +512,9 @@ int tegra_dpaux_detach(struct tegra_dpaux *dpaux) return -ETIMEDOUT; } -enum drm_connector_status tegra_dpaux_detect(struct tegra_dpaux *dpaux) +enum drm_connector_status drm_dp_aux_detect(struct drm_dp_aux *aux) { + struct tegra_dpaux *dpaux = to_dpaux(aux); u32 value; value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXSTAT); @@ -522,8 +525,9 @@ enum drm_connector_status tegra_dpaux_detect(struct tegra_dpaux *dpaux) return connector_status_disconnected; } -int tegra_dpaux_enable(struct tegra_dpaux *dpaux) +int drm_dp_aux_enable(struct drm_dp_aux *aux) { + struct tegra_dpaux *dpaux = to_dpaux(aux); u32 value; value = DPAUX_HYBRID_PADCTL_AUX_CMH(2) | @@ -540,8 +544,9 @@ int tegra_dpaux_enable(struct tegra_dpaux *dpaux) return 0; } -int tegra_dpaux_disable(struct tegra_dpaux *dpaux) +int drm_dp_aux_disable(struct drm_dp_aux *aux) { + struct tegra_dpaux *dpaux = to_dpaux(aux); u32 value; value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE); @@ -551,11 +556,11 @@ int tegra_dpaux_disable(struct tegra_dpaux *dpaux) return 0; } -int tegra_dpaux_prepare(struct tegra_dpaux *dpaux, u8 encoding) +int drm_dp_aux_prepare(struct drm_dp_aux *aux, u8 encoding) { int err; - err = drm_dp_dpcd_writeb(&dpaux->aux, DP_MAIN_LINK_CHANNEL_CODING_SET, + err = drm_dp_dpcd_writeb(aux, DP_MAIN_LINK_CHANNEL_CODING_SET, encoding); if (err < 0) return err; @@ -563,15 +568,15 @@ int tegra_dpaux_prepare(struct tegra_dpaux *dpaux, u8 encoding) return 0; } -int tegra_dpaux_train(struct tegra_dpaux *dpaux, struct drm_dp_link *link, - u8 pattern) +int drm_dp_aux_train(struct drm_dp_aux *aux, struct drm_dp_link *link, + u8 pattern) { u8 tp = pattern & DP_TRAINING_PATTERN_MASK; u8 status[DP_LINK_STATUS_SIZE], values[4]; unsigned int i; int err; - err = drm_dp_dpcd_writeb(&dpaux->aux, DP_TRAINING_PATTERN_SET, pattern); + err = drm_dp_dpcd_writeb(aux, DP_TRAINING_PATTERN_SET, pattern); if (err < 0) return err; @@ -584,14 +589,14 @@ int tegra_dpaux_train(struct tegra_dpaux *dpaux, struct drm_dp_link *link, DP_TRAIN_MAX_SWING_REACHED | DP_TRAIN_VOLTAGE_SWING_LEVEL_0; - err = drm_dp_dpcd_write(&dpaux->aux, DP_TRAINING_LANE0_SET, values, + err = drm_dp_dpcd_write(aux, DP_TRAINING_LANE0_SET, values, link->num_lanes); if (err < 0) return err; usleep_range(500, 1000); - err = drm_dp_dpcd_read_link_status(&dpaux->aux, status); + err = drm_dp_dpcd_read_link_status(aux, status); if (err < 0) return err; @@ -609,11 +614,11 @@ int tegra_dpaux_train(struct tegra_dpaux *dpaux, struct drm_dp_link *link, break; default: - dev_err(dpaux->dev, "unsupported training pattern %u\n", tp); + dev_err(aux->dev, "unsupported training pattern %u\n", tp); return -EINVAL; } - err = drm_dp_dpcd_writeb(&dpaux->aux, DP_EDP_CONFIGURATION_SET, 0); + err = drm_dp_dpcd_writeb(aux, DP_EDP_CONFIGURATION_SET, 0); if (err < 0) return err; diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index a9728146d846..5bf696c993ac 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -247,18 +247,17 @@ void tegra_output_connector_destroy(struct drm_connector *connector); void tegra_output_encoder_destroy(struct drm_encoder *encoder); /* from dpaux.c */ -struct tegra_dpaux; struct drm_dp_link; -struct tegra_dpaux *tegra_dpaux_find_by_of_node(struct device_node *np); -enum drm_connector_status tegra_dpaux_detect(struct tegra_dpaux *dpaux); -int tegra_dpaux_attach(struct tegra_dpaux *dpaux, struct tegra_output *output); -int tegra_dpaux_detach(struct tegra_dpaux *dpaux); -int tegra_dpaux_enable(struct tegra_dpaux *dpaux); -int tegra_dpaux_disable(struct tegra_dpaux *dpaux); -int tegra_dpaux_prepare(struct tegra_dpaux *dpaux, u8 encoding); -int tegra_dpaux_train(struct tegra_dpaux *dpaux, struct drm_dp_link *link, - u8 pattern); +struct drm_dp_aux *drm_dp_aux_find_by_of_node(struct device_node *np); +enum drm_connector_status drm_dp_aux_detect(struct drm_dp_aux *aux); +int drm_dp_aux_attach(struct drm_dp_aux *aux, struct tegra_output *output); +int drm_dp_aux_detach(struct drm_dp_aux *aux); +int drm_dp_aux_enable(struct drm_dp_aux *aux); +int drm_dp_aux_disable(struct drm_dp_aux *aux); +int drm_dp_aux_prepare(struct drm_dp_aux *aux, u8 encoding); +int drm_dp_aux_train(struct drm_dp_aux *aux, struct drm_dp_link *link, + u8 pattern); /* from fb.c */ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer, diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 3eff7cf75d25..1414e20a0b71 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -173,7 +173,7 @@ struct tegra_sor { struct clk *clk_dp; struct clk *clk; - struct tegra_dpaux *dpaux; + struct drm_dp_aux *aux; struct drm_info_list *debugfs_files; struct drm_minor *minor; @@ -273,7 +273,7 @@ static int tegra_sor_dp_train_fast(struct tegra_sor *sor, SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0); tegra_sor_writel(sor, value, SOR_DP_PADCTL0); - err = tegra_dpaux_prepare(sor->dpaux, DP_SET_ANSI_8B10B); + err = drm_dp_aux_prepare(sor->aux, DP_SET_ANSI_8B10B); if (err < 0) return err; @@ -288,7 +288,7 @@ static int tegra_sor_dp_train_fast(struct tegra_sor *sor, pattern = DP_TRAINING_PATTERN_1; - err = tegra_dpaux_train(sor->dpaux, link, pattern); + err = drm_dp_aux_train(sor->aux, link, pattern); if (err < 0) return err; @@ -309,7 +309,7 @@ static int tegra_sor_dp_train_fast(struct tegra_sor *sor, pattern = DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_2; - err = tegra_dpaux_train(sor->dpaux, link, pattern); + err = drm_dp_aux_train(sor->aux, link, pattern); if (err < 0) return err; @@ -324,7 +324,7 @@ static int tegra_sor_dp_train_fast(struct tegra_sor *sor, pattern = DP_TRAINING_PATTERN_DISABLE; - err = tegra_dpaux_train(sor->dpaux, link, pattern); + err = drm_dp_aux_train(sor->aux, link, pattern); if (err < 0) return err; @@ -1044,8 +1044,8 @@ tegra_sor_connector_detect(struct drm_connector *connector, bool force) struct tegra_output *output = connector_to_output(connector); struct tegra_sor *sor = to_sor(output); - if (sor->dpaux) - return tegra_dpaux_detect(sor->dpaux); + if (sor->aux) + return drm_dp_aux_detect(sor->aux); return tegra_output_connector_detect(connector, force); } @@ -1066,13 +1066,13 @@ static int tegra_sor_connector_get_modes(struct drm_connector *connector) struct tegra_sor *sor = to_sor(output); int err; - if (sor->dpaux) - tegra_dpaux_enable(sor->dpaux); + if (sor->aux) + drm_dp_aux_enable(sor->aux); err = tegra_output_connector_get_modes(connector); - if (sor->dpaux) - tegra_dpaux_disable(sor->dpaux); + if (sor->aux) + drm_dp_aux_disable(sor->aux); return err; } @@ -1128,8 +1128,8 @@ static void tegra_sor_edp_disable(struct drm_encoder *encoder) if (err < 0) dev_err(sor->dev, "failed to power down SOR: %d\n", err); - if (sor->dpaux) { - err = tegra_dpaux_disable(sor->dpaux); + if (sor->aux) { + err = drm_dp_aux_disable(sor->aux); if (err < 0) dev_err(sor->dev, "failed to disable DP: %d\n", err); } @@ -1196,7 +1196,6 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) struct tegra_sor *sor = to_sor(output); struct tegra_sor_config config; struct drm_dp_link link; - struct drm_dp_aux *aux; int err = 0; u32 value; @@ -1209,15 +1208,12 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) if (output->panel) drm_panel_prepare(output->panel); - /* FIXME: properly convert to struct drm_dp_aux */ - aux = (struct drm_dp_aux *)sor->dpaux; - - if (sor->dpaux) { - err = tegra_dpaux_enable(sor->dpaux); + if (sor->aux) { + err = drm_dp_aux_enable(sor->aux); if (err < 0) dev_err(sor->dev, "failed to enable DP: %d\n", err); - err = drm_dp_link_probe(aux, &link); + err = drm_dp_link_probe(sor->aux, &link); if (err < 0) { dev_err(sor->dev, "failed to probe eDP link: %d\n", err); @@ -1434,20 +1430,20 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) value |= SOR_DP_PADCTL_PAD_CAL_PD; tegra_sor_writel(sor, value, SOR_DP_PADCTL0); - if (sor->dpaux) { + if (sor->aux) { u8 rate, lanes; - err = drm_dp_link_probe(aux, &link); + err = drm_dp_link_probe(sor->aux, &link); if (err < 0) dev_err(sor->dev, "failed to probe eDP link: %d\n", err); - err = drm_dp_link_power_up(aux, &link); + err = drm_dp_link_power_up(sor->aux, &link); if (err < 0) dev_err(sor->dev, "failed to power up eDP link: %d\n", err); - err = drm_dp_link_configure(aux, &link); + err = drm_dp_link_configure(sor->aux, &link); if (err < 0) dev_err(sor->dev, "failed to configure eDP link: %d\n", err); @@ -2148,7 +2144,7 @@ static int tegra_sor_init(struct host1x_client *client) int encoder = DRM_MODE_ENCODER_NONE; int err; - if (!sor->dpaux) { + if (!sor->aux) { if (sor->soc->supports_hdmi) { connector = DRM_MODE_CONNECTOR_HDMIA; encoder = DRM_MODE_ENCODER_TMDS; @@ -2199,8 +2195,8 @@ static int tegra_sor_init(struct host1x_client *client) dev_err(sor->dev, "debugfs setup failed: %d\n", err); } - if (sor->dpaux) { - err = tegra_dpaux_attach(sor->dpaux, &sor->output); + if (sor->aux) { + err = drm_dp_aux_attach(sor->aux, &sor->output); if (err < 0) { dev_err(sor->dev, "failed to attach DP: %d\n", err); return err; @@ -2249,8 +2245,8 @@ static int tegra_sor_exit(struct host1x_client *client) tegra_output_exit(&sor->output); - if (sor->dpaux) { - err = tegra_dpaux_detach(sor->dpaux); + if (sor->aux) { + err = drm_dp_aux_detach(sor->aux); if (err < 0) { dev_err(sor->dev, "failed to detach DP: %d\n", err); return err; @@ -2399,14 +2395,14 @@ static int tegra_sor_probe(struct platform_device *pdev) np = of_parse_phandle(pdev->dev.of_node, "nvidia,dpaux", 0); if (np) { - sor->dpaux = tegra_dpaux_find_by_of_node(np); + sor->aux = drm_dp_aux_find_by_of_node(np); of_node_put(np); - if (!sor->dpaux) + if (!sor->aux) return -EPROBE_DEFER; } - if (!sor->dpaux) { + if (!sor->aux) { if (sor->soc->supports_hdmi) { sor->ops = &tegra_sor_hdmi_ops; } else if (sor->soc->supports_lvds) { From 01b9bea0c2616e92334cfa3e052862527bf25d36 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 11 Nov 2015 17:15:29 +0100 Subject: [PATCH 09/14] drm/tegra: sor: Remove unnecessary conditional Checking for sor->aux in eDP specific code is unnecessary because eDP inherently requires a valid AUX channel. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/sor.c | 97 ++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 54 deletions(-) diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 1414e20a0b71..17e008f6081f 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -1196,6 +1196,7 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) struct tegra_sor *sor = to_sor(output); struct tegra_sor_config config; struct drm_dp_link link; + u8 rate, lanes; int err = 0; u32 value; @@ -1208,17 +1209,14 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) if (output->panel) drm_panel_prepare(output->panel); - if (sor->aux) { - err = drm_dp_aux_enable(sor->aux); - if (err < 0) - dev_err(sor->dev, "failed to enable DP: %d\n", err); + err = drm_dp_aux_enable(sor->aux); + if (err < 0) + dev_err(sor->dev, "failed to enable DP: %d\n", err); - err = drm_dp_link_probe(sor->aux, &link); - if (err < 0) { - dev_err(sor->dev, "failed to probe eDP link: %d\n", - err); - return; - } + err = drm_dp_link_probe(sor->aux, &link); + if (err < 0) { + dev_err(sor->dev, "failed to probe eDP link: %d\n", err); + return; } err = clk_set_parent(sor->clk, sor->clk_safe); @@ -1430,61 +1428,52 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) value |= SOR_DP_PADCTL_PAD_CAL_PD; tegra_sor_writel(sor, value, SOR_DP_PADCTL0); - if (sor->aux) { - u8 rate, lanes; + err = drm_dp_link_probe(sor->aux, &link); + if (err < 0) + dev_err(sor->dev, "failed to probe eDP link: %d\n", err); - err = drm_dp_link_probe(sor->aux, &link); - if (err < 0) - dev_err(sor->dev, "failed to probe eDP link: %d\n", - err); + err = drm_dp_link_power_up(sor->aux, &link); + if (err < 0) + dev_err(sor->dev, "failed to power up eDP link: %d\n", err); - err = drm_dp_link_power_up(sor->aux, &link); - if (err < 0) - dev_err(sor->dev, "failed to power up eDP link: %d\n", - err); + err = drm_dp_link_configure(sor->aux, &link); + if (err < 0) + dev_err(sor->dev, "failed to configure eDP link: %d\n", err); - err = drm_dp_link_configure(sor->aux, &link); - if (err < 0) - dev_err(sor->dev, "failed to configure eDP link: %d\n", - err); + rate = drm_dp_link_rate_to_bw_code(link.rate); + lanes = link.num_lanes; - rate = drm_dp_link_rate_to_bw_code(link.rate); - lanes = link.num_lanes; + value = tegra_sor_readl(sor, SOR_CLK_CNTRL); + value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK; + value |= SOR_CLK_CNTRL_DP_LINK_SPEED(rate); + tegra_sor_writel(sor, value, SOR_CLK_CNTRL); - value = tegra_sor_readl(sor, SOR_CLK_CNTRL); - value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK; - value |= SOR_CLK_CNTRL_DP_LINK_SPEED(rate); - tegra_sor_writel(sor, value, SOR_CLK_CNTRL); + value = tegra_sor_readl(sor, SOR_DP_LINKCTL0); + value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK; + value |= SOR_DP_LINKCTL_LANE_COUNT(lanes); - value = tegra_sor_readl(sor, SOR_DP_LINKCTL0); - value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK; - value |= SOR_DP_LINKCTL_LANE_COUNT(lanes); + if (link.capabilities & DP_LINK_CAP_ENHANCED_FRAMING) + value |= SOR_DP_LINKCTL_ENHANCED_FRAME; - if (link.capabilities & DP_LINK_CAP_ENHANCED_FRAMING) - value |= SOR_DP_LINKCTL_ENHANCED_FRAME; + tegra_sor_writel(sor, value, SOR_DP_LINKCTL0); - tegra_sor_writel(sor, value, SOR_DP_LINKCTL0); + /* disable training pattern generator */ - /* disable training pattern generator */ - - for (i = 0; i < link.num_lanes; i++) { - unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | - SOR_DP_TPG_SCRAMBLER_GALIOS | - SOR_DP_TPG_PATTERN_NONE; - value = (value << 8) | lane; - } - - tegra_sor_writel(sor, value, SOR_DP_TPG); - - err = tegra_sor_dp_train_fast(sor, &link); - if (err < 0) { - dev_err(sor->dev, "DP fast link training failed: %d\n", - err); - } - - dev_dbg(sor->dev, "fast link training succeeded\n"); + for (i = 0; i < link.num_lanes; i++) { + unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | + SOR_DP_TPG_SCRAMBLER_GALIOS | + SOR_DP_TPG_PATTERN_NONE; + value = (value << 8) | lane; } + tegra_sor_writel(sor, value, SOR_DP_TPG); + + err = tegra_sor_dp_train_fast(sor, &link); + if (err < 0) + dev_err(sor->dev, "DP fast link training failed: %d\n", err); + + dev_dbg(sor->dev, "fast link training succeeded\n"); + err = tegra_sor_power_up(sor, 250); if (err < 0) dev_err(sor->dev, "failed to power up SOR: %d\n", err); From 986c58d1625df222c6148c1dc72e59fbf96ef75e Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 11 Aug 2015 13:11:49 +0200 Subject: [PATCH 10/14] drm/tegra: Implement subsystem-level suspend/resume Use the drm_atomic_helper_suspend() and drm_atomic_helper_resume() helpers to implement subsystem-level suspend/resume. v2: suspend framebuffer device to avoid concurrency issues v3: resume fbdev on failure to suspend (Emil Velikov) Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/drm.c | 12 ++++++++++++ drivers/gpu/drm/tegra/drm.h | 4 ++++ drivers/gpu/drm/tegra/fb.c | 24 ++++++++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index c4910d528411..ced5a095d2ad 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -1021,8 +1021,17 @@ static int host1x_drm_remove(struct host1x_device *dev) static int host1x_drm_suspend(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); + struct tegra_drm *tegra = drm->dev_private; drm_kms_helper_poll_disable(drm); + tegra_drm_fb_suspend(drm); + + tegra->state = drm_atomic_helper_suspend(drm); + if (IS_ERR(tegra->state)) { + tegra_drm_fb_resume(drm); + drm_kms_helper_poll_enable(drm); + return PTR_ERR(tegra->state); + } return 0; } @@ -1030,7 +1039,10 @@ static int host1x_drm_suspend(struct device *dev) static int host1x_drm_resume(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); + struct tegra_drm *tegra = drm->dev_private; + drm_atomic_helper_resume(drm, tegra->state); + tegra_drm_fb_resume(drm); drm_kms_helper_poll_enable(drm); return 0; diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 5bf696c993ac..c088f2f67eda 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -57,6 +57,8 @@ struct tegra_drm { struct work_struct work; struct mutex lock; } commit; + + struct drm_atomic_state *state; }; struct tegra_drm_client; @@ -272,6 +274,8 @@ int tegra_drm_fb_prepare(struct drm_device *drm); void tegra_drm_fb_free(struct drm_device *drm); int tegra_drm_fb_init(struct drm_device *drm); void tegra_drm_fb_exit(struct drm_device *drm); +void tegra_drm_fb_suspend(struct drm_device *drm); +void tegra_drm_fb_resume(struct drm_device *drm); #ifdef CONFIG_DRM_FBDEV_EMULATION void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev); void tegra_fb_output_poll_changed(struct drm_device *drm); diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index ede9e94f3312..a7213f30fb20 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -10,6 +10,8 @@ * published by the Free Software Foundation. */ +#include + #include "drm.h" #include "gem.h" @@ -413,3 +415,25 @@ void tegra_drm_fb_exit(struct drm_device *drm) tegra_fbdev_exit(tegra->fbdev); #endif } + +void tegra_drm_fb_suspend(struct drm_device *drm) +{ +#ifdef CONFIG_DRM_FBDEV_EMULATION + struct tegra_drm *tegra = drm->dev_private; + + console_lock(); + drm_fb_helper_set_suspend(&tegra->fbdev->base, 1); + console_unlock(); +#endif +} + +void tegra_drm_fb_resume(struct drm_device *drm) +{ +#ifdef CONFIG_DRM_FBDEV_EMULATION + struct tegra_drm *tegra = drm->dev_private; + + console_lock(); + drm_fb_helper_set_suspend(&tegra->fbdev->base, 0); + console_unlock(); +#endif +} From cf6b17445dbf6c287fc389c9a9cb2276984b6a5f Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 24 Oct 2015 16:42:31 +0200 Subject: [PATCH 11/14] drm/tegra: dc: Add missing of_node_put() for_each_matching_node() performs an of_node_get() on each iteration, so a break out of the loop requires an of_node_put(). A simplified version of the semantic patch that fixes this problem is as follows (http://coccinelle.lip6.fr): // @@ local idexpression n; expression e; @@ for_each_matching_node(n,...) { ... ( of_node_put(n); | e = n | + of_node_put(n); ? break; ) ... } ... when != n // Signed-off-by: Julia Lawall Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index e9f24a85a103..ea174b5a649d 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1952,8 +1952,10 @@ static int tegra_dc_parse_dt(struct tegra_dc *dc) * cases where only a single display controller is used. */ for_each_matching_node(np, tegra_dc_of_match) { - if (np == dc->dev->of_node) + if (np == dc->dev->of_node) { + of_node_put(np); break; + } value++; } From db8b42fbfc6f778fa22201f24e53fc4250b52e4c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 17 Aug 2015 17:37:03 +0300 Subject: [PATCH 12/14] drm/tegra: checking for IS_ERR() instead of NULL The tegra_sor_hdmi_find_settings() function returns NULL on error and not an ERR_PTR. Fixes: 459cc2c6800b ('drm/tegra: sor: Add HDMI support') Signed-off-by: Dan Carpenter Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/sor.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 17e008f6081f..a5fddd3d06a9 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -1946,9 +1946,9 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) /* production settings */ settings = tegra_sor_hdmi_find_settings(sor, mode->clock * 1000); - if (IS_ERR(settings)) { - dev_err(sor->dev, "no settings for pixel clock %d Hz: %ld\n", - mode->clock * 1000, PTR_ERR(settings)); + if (!settings) { + dev_err(sor->dev, "no settings for pixel clock %d Hz\n", + mode->clock * 1000); return; } From d2d8c3581850dc1b21d903b0680d0e3358d52ae2 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 23 Nov 2015 16:46:30 +0100 Subject: [PATCH 13/14] drm/tegra: Use DRIVER level for IOMMU aperture message This allows the message to be shown even if core messages are disabled globally in DRM. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/drm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index ced5a095d2ad..d9a09251e9f1 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -137,8 +137,8 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) start = geometry->aperture_start; end = geometry->aperture_end; - DRM_DEBUG("IOMMU context initialized (aperture: %#llx-%#llx)\n", - start, end); + DRM_DEBUG_DRIVER("IOMMU aperture initialized (%#llx-%#llx)\n", + start, end); drm_mm_init(&tegra->mm, start, end - start + 1); } From ad906599c1919eda6f365ecec8d50198c9232131 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 24 Sep 2015 18:38:09 +0200 Subject: [PATCH 14/14] drm/tegra: Advertise DRIVER_ATOMIC The driver has supported atomic mode-setting for quite a while. It's time to advertise that. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/drm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index d9a09251e9f1..50d7160c667b 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -919,7 +919,8 @@ static void tegra_debugfs_cleanup(struct drm_minor *minor) #endif static struct drm_driver tegra_drm_driver = { - .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | + DRIVER_ATOMIC, .load = tegra_drm_load, .unload = tegra_drm_unload, .open = tegra_drm_open,