drm/msm: what's in store for 3.15
Subsystems (5)
cover drm/msm: what's in store for 3.15
An early look at what I have lined up for 3.15. Main highlights are:
* HDMI audio support (sans interface to ALSA which needs to be invented)
* Basic GPU power management
* Add chip-id param, so userspace can know the GPU patch revision level (which gallium driver needs in a couple places)
* Convert over to use new componentised device support and get rid of global pdev ptrs
Rob Clark (7):
drm/msm: hdmi audio support
drm/msm: add hang_debug module param
drm/msm: spin helper
drm/msm: crank down gpu when inactive
drm/msm: add chip-id param
drm/msm: use componentised device support
drm/msm: validate flags, etc
drivers/gpu/drm/msm/Makefile | 1 + drivers/gpu/drm/msm/adreno/a3xx_gpu.c | 104 +++++++++--- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 65 +++++--- drivers/gpu/drm/msm/adreno/adreno_gpu.h | 16 +- drivers/gpu/drm/msm/hdmi/hdmi.c | 49 ++++-- drivers/gpu/drm/msm/hdmi/hdmi.h | 25 +++ drivers/gpu/drm/msm/hdmi/hdmi_audio.c | 273 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 26 ++- drivers/gpu/drm/msm/msm_drv.c | 160 ++++++++++++++++++- drivers/gpu/drm/msm/msm_drv.h | 4 + drivers/gpu/drm/msm/msm_gem_submit.c | 15 +- drivers/gpu/drm/msm/msm_gpu.c | 85 +++++++++- drivers/gpu/drm/msm/msm_gpu.h | 16 +- include/uapi/drm/msm_drm.h | 12 ++ 14 files changed, 759 insertions(+), 92 deletions(-) create mode 100644 drivers/gpu/drm/msm/hdmi/hdmi_audio.c
Signature · 1 line
1.8.5.3
PATCH 1/7 drm/msm: hdmi audio support
--- drivers/gpu/drm/msm/Makefile | 1 + drivers/gpu/drm/msm/hdmi/hdmi.c | 8 +- drivers/gpu/drm/msm/hdmi/hdmi.h | 25 +++ drivers/gpu/drm/msm/hdmi/hdmi_audio.c | 273 +++++++++++++++++++++++++++++++++ drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 26 ++-- 5 files changed, 317 insertions(+), 16 deletions(-) create mode 100644 drivers/gpu/drm/msm/hdmi/hdmi_audio.c
drivers/gpu/drm/msm/Makefile +1 -0
@@ -7,6 +7,7 @@ msm-y := \ adreno/adreno_gpu.o \ adreno/a3xx_gpu.o \ hdmi/hdmi.o \ + hdmi/hdmi_audio.o \ hdmi/hdmi_bridge.o \ hdmi/hdmi_connector.o \ hdmi/hdmi_i2c.o \
drivers/gpu/drm/msm/hdmi/hdmi.c +7 -1
@@ -67,6 +67,8 @@ void hdmi_destroy(struct kref *kref)if (hdmi->i2c)hdmi_i2c_destroy (hdmi->i2c); +platform_set_drvdata (hdmi->pdev,NULL ); +put_device (&hdmi->pdev->dev); }
@@ -102,6 +104,8 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder) hdmi->config = config; hdmi->encoder = encoder; +hdmi_audio_infoframe_init (&hdmi->audio.infoframe); +/* not sure about which phy maps to which msm.. probably I miss some */ if (config->phy_init) hdmi->phy = config->phy_init (hdmi);
@@ -228,6 +232,8 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder) priv->bridges[priv->num_bridges++] = hdmi->bridge; priv->connectors[priv->num_connectors++] = hdmi->connector; +platform_set_drvdata (pdev, hdmi); +return hdmi; fail:
@@ -305,7 +311,7 @@ static int hdmi_dev_probe(struct platform_device *pdev) config.ddc_data_gpio =71 ; config.hpd_gpio =72 ; config.mux_en_gpio =-1 ; - config.mux_sel_gpio =13 +NR_GPIO_IRQS ; + config.mux_sel_gpio =-1 ; }else if (cpu_is_msm8960 () ||cpu_is_msm8960ab ()) {static const char *hpd_reg_names[] = {"8921_hdmi_mvs" }; config.phy_init = hdmi_phy_8960_init;
drivers/gpu/drm/msm/hdmi/hdmi.h +25 -0
@@ -22,6 +22,7 @@#include <linux/clk.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> +#include <linux/hdmi.h> #include "msm_drv.h" #include "hdmi.xml.h"
@@ -30,6 +31,12 @@struct hdmi_phy ;struct hdmi_platform_config ; +struct hdmi_audio { +bool enabled; +struct hdmi_audio_infoframe infoframe; +int rate; +}; +struct hdmi {struct kref refcount;
@@ -38,6 +45,13 @@ struct hdmi {const struct hdmi_platform_config *config; +/* audio state: */ +struct hdmi_audio audio; + +/* video state: */ +bool power_on; +unsigned long int pixclock; +void __iomem *mmio;struct regulator *hpd_regs[2 ];
@@ -132,6 +146,17 @@ struct hdmi_phy *hdmi_phy_8x60_init(struct hdmi *hdmi);struct hdmi_phy *hdmi_phy_8x74_init (struct hdmi *hdmi);/* +* audio: +*/ + +int hdmi_audio_update (struct hdmi *hdmi); +int hdmi_audio_info_setup (struct hdmi *hdmi,bool enabled, +uint32_t num_of_channels,uint32_t channel_allocation, +uint32_t level_shift,bool down_mix); +void hdmi_audio_set_sample_rate (struct hdmi *hdmi,int rate); + + +/* * hdmi bridge: */
drivers/gpu/drm/msm/hdmi/hdmi_audio.c new file +273 -0
273 lines not on this page · raw message
drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +11 -15
@@ -19,11 +19,7 @@struct hdmi_bridge {struct drm_bridge base; -struct hdmi *hdmi; -bool power_on; - -unsigned long int pixclock; };#define to_hdmi_bridge (x) container_of(x, struct hdmi_bridge, base)
@@ -52,8 +48,8 @@ static void power_on(struct drm_bridge *bridge) }if (config->pwr_clk_cnt >0 ) { -DBG ("pixclock: %lu" , hdmi_bridge->pixclock); - ret =clk_set_rate (hdmi->pwr_clks[0 ], hdmi_bridge->pixclock); +DBG ("pixclock: %lu" , hdmi->pixclock); + ret =clk_set_rate (hdmi->pwr_clks[0 ], hdmi->pixclock);if (ret) {dev_err (dev->dev,"failed to set pixel clk: %s (%d)\n" , config->pwr_clk_names[0 ], ret);
@@ -102,12 +98,13 @@ static void hdmi_bridge_pre_enable(struct drm_bridge *bridge)DBG ("power up" ); -if (!hdmi_bridge->power_on) { +if (!hdmi->power_on) {power_on (bridge); - hdmi_bridge->power_on = true; + hdmi->power_on = true; +hdmi_audio_update (hdmi); } - phy->funcs->powerup (phy, hdmi_bridge->pixclock); + phy->funcs->powerup (phy, hdmi->pixclock);hdmi_set_mode (hdmi, true); }
@@ -129,9 +126,10 @@ static void hdmi_bridge_post_disable(struct drm_bridge *bridge)hdmi_set_mode (hdmi, false); phy->funcs->powerdown (phy); -if (hdmi_bridge->power_on) { +if (hdmi->power_on) {power_off (bridge); - hdmi_bridge->power_on = false; + hdmi->power_on = false; +hdmi_audio_update (hdmi); } }
@@ -146,7 +144,7 @@ static void hdmi_bridge_mode_set(struct drm_bridge *bridge, mode = adjusted_mode; - hdmi_bridge->pixclock = mode->clock *1000 ; + hdmi->pixclock = mode->clock *1000 ; hdmi->hdmi_mode =drm_match_cea_mode (mode) >1 ;
@@ -194,9 +192,7 @@ static void hdmi_bridge_mode_set(struct drm_bridge *bridge,DBG ("frame_ctrl=%08x" , frame_ctrl);hdmi_write (hdmi,REG_HDMI_FRAME_CTRL , frame_ctrl); -// TODO until we have audio, this might be safest: -if (hdmi->hdmi_mode) -hdmi_write (hdmi,REG_HDMI_GC ,HDMI_GC_MUTE ); +hdmi_audio_update (hdmi); }static const struct drm_bridge_funcs hdmi_bridge_funcs = {
Signature · 1 line
1.8.5.3
PATCH 2/7 drm/msm: add hang_debug module param
In reply to Rob Clarkmsm.hang_debug=y will dump out current register values if the gpu locks up, for easier debugging.
--- drivers/gpu/drm/msm/adreno/a3xx_gpu.c | 34 ++++++++++++++++++++++++++++++++- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 18 +++++++++++++++++ drivers/gpu/drm/msm/adreno/adreno_gpu.h | 1 + 3 files changed, 52 insertions(+), 1 deletion(-)
drivers/gpu/drm/msm/adreno/a3xx_gpu.c +33 -1
@@ -35,6 +35,12 @@ A3XX_INT0_CP_AHB_ERROR_HALT | \ A3XX_INT0_UCHE_OOB_ACCESS) + +static bool hang_debug = false; +MODULE_PARM_DESC (hang_debug,"Dump registers when hang is detected (can be slow!)" ); +module_param_named (hang_debug, hang_debug, bool,0600 ); +static void a3xx_dump (struct msm_gpu *gpu); +static struct platform_device *a3xx_pdev;static void a3xx_me_init (struct msm_gpu *gpu)
@@ -291,6 +297,9 @@ static int a3xx_hw_init(struct msm_gpu *gpu)static void a3xx_recover (struct msm_gpu *gpu) { +/* dump registers before resetting gpu, if enabled: */ +if (hang_debug) +a3xx_dump (gpu);gpu_write (gpu,REG_A3XX_RBBM_SW_RESET_CMD ,1 );gpu_read (gpu,REG_A3XX_RBBM_SW_RESET_CMD );gpu_write (gpu,REG_A3XX_RBBM_SW_RESET_CMD ,0 );
@@ -352,7 +361,6 @@ static irqreturn_t a3xx_irq(struct msm_gpu *gpu)return IRQ_HANDLED ; } -#ifdef CONFIG_DEBUG_FS static const unsigned int a3xx_registers[] = {0x0000 ,0x0002 ,0x0010 ,0x0012 ,0x0018 ,0x0018 ,0x0020 ,0x0027 ,0x0029 ,0x002b ,0x002e ,0x0033 ,0x0040 ,0x0042 ,0x0050 ,0x005c ,
@@ -392,6 +400,7 @@ static const unsigned int a3xx_registers[] = { 0x303c, 0x303c, 0x305e, 0x305f, }; +#ifdef CONFIG_DEBUG_FS static void a3xx_show (struct msm_gpu *gpu,struct seq_file *m) {int i;
@@ -415,6 +424,29 @@ static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m) }#endif +/* would be nice to not have to duplicate the _show() stuff with printk(): */ +static void a3xx_dump (struct msm_gpu *gpu) +{ +int i; + +adreno_dump (gpu); +printk ("status: %08x\n" , +gpu_read (gpu,REG_A3XX_RBBM_STATUS )); + +/* dump these out in a form that can be parsed by demsm: */ +printk ("IO:region %s 00000000 00020000\n" , gpu->name); +for (i =0 ; i <ARRAY_SIZE (a3xx_registers); i +=2 ) { +uint32_t start = a3xx_registers[i]; +uint32_t end = a3xx_registers[i+1 ]; +uint32_t addr; + +for (addr = start; addr <= end; addr++) { +uint32_t val =gpu_read (gpu, addr); +printk ("IO:R %08x %08x\n" , addr<<2 , val); + } + } +} +static const struct adreno_gpu_funcs funcs = { .base = { .get_param = adreno_get_param,
drivers/gpu/drm/msm/adreno/adreno_gpu.c +18 -0
@@ -260,6 +260,24 @@ void adreno_show(struct msm_gpu *gpu, struct seq_file *m) }#endif +/* would be nice to not have to duplicate the _show() stuff with printk(): */ +void adreno_dump (struct msm_gpu *gpu) +{ +struct adreno_gpu *adreno_gpu =to_adreno_gpu (gpu); + +printk ("revision: %d (%d.%d.%d.%d)\n" , + adreno_gpu->info->revn, adreno_gpu->rev.core, + adreno_gpu->rev.major, adreno_gpu->rev.minor, + adreno_gpu->rev.patchid); + +printk ("fence: %d/%d\n" , adreno_gpu->memptrs->fence, + gpu->submitted_fence); +printk ("rptr: %d\n" , adreno_gpu->memptrs->rptr); +printk ("wptr: %d\n" , adreno_gpu->memptrs->wptr); +printk ("rb wptr: %d\n" ,get_wptr (gpu->rb)); + +} +void adreno_wait_ring (struct msm_gpu *gpu,uint32_t ndwords) {struct adreno_gpu *adreno_gpu =to_adreno_gpu (gpu);
drivers/gpu/drm/msm/adreno/adreno_gpu.h +1 -0
@@ -114,6 +114,7 @@ void adreno_idle(struct msm_gpu *gpu);#ifdef CONFIG_DEBUG_FS void adreno_show (struct msm_gpu *gpu,struct seq_file *m);#endif +void adreno_dump (struct msm_gpu *gpu);void adreno_wait_ring (struct msm_gpu *gpu,uint32_t ndwords); int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
Signature · 1 line
1.8.5.3
Re: [PATCH 2/7] drm/msm: add hang_debug module param
Quoted 28 lines · Rob Clark
msm.hang_debug=y will dump out current register values if the gpu locks up, for easier debugging.
Signed-off-by Rob Clark <[email protected]>--- drivers/gpu/drm/msm/adreno/a3xx_gpu.c | 34 ++++++++++++++++++++++++++++++++- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 18 +++++++++++++++++ drivers/gpu/drm/msm/adreno/adreno_gpu.h | 1 + 3 files changed, 52 insertions(+), 1 deletion(-)drivers/gpu/drm/msm/adreno/a3xx_gpu.c +6
-0@@ -35,6 +35,12 @@ A3XX_INT0_CP_AHB_ERROR_HALT | \ A3XX_INT0_UCHE_OOB_ACCESS) + +static bool hang_debug = false; +MODULE_PARM_DESC (hang_debug,"Dump registers when hang is detected (can be slow!)" ); +module_param_named (hang_debug, hang_debug, bool,0600 ); +static void a3xx_dump (struct msm_gpu *gpu); +static struct platform_device *a3xx_pdev;static void a3xx_me_init (struct msm_gpu *gpu)
drivers/gpu/drm/msm/adreno/a3xx_gpu.c static int a3xx_hw_init(struct msm_gpu *gpu) · line 302
Quoted 6 lines
drivers/gpu/drm/msm/adreno/a3xx_gpu.c +3
-0@@ -291,6 +297,9 @@ static int a3xx_hw_init(struct msm_gpu *gpu)static void a3xx_recover (struct msm_gpu *gpu) { +/* dump registers before resetting gpu, if enabled: */ +if (hang_debug) +a3xx_dump (gpu);
You will need to be careful here - not all the registers are suitable for reading back. On some flavors of A330 in particular some HLSQ registers reads will bring the system down. You can see the gory details in the KGSL driver. If this works for you for now great but fair warning that some cores might have different experiences.
Quoted 65 lines
drivers/gpu/drm/msm/adreno/a3xx_gpu.c +24
-1gpu_write (gpu,REG_A3XX_RBBM_SW_RESET_CMD ,1 );gpu_read (gpu,REG_A3XX_RBBM_SW_RESET_CMD );gpu_write (gpu,REG_A3XX_RBBM_SW_RESET_CMD ,0 );@@ -352,7 +361,6 @@ static irqreturn_t a3xx_irq(struct msm_gpu *gpu)return IRQ_HANDLED ; } -#ifdef CONFIG_DEBUG_FS static const unsigned int a3xx_registers[] = {0x0000 ,0x0002 ,0x0010 ,0x0012 ,0x0018 ,0x0018 ,0x0020 ,0x0027 ,0x0029 ,0x002b ,0x002e ,0x0033 ,0x0040 ,0x0042 ,0x0050 ,0x005c ,@@ -392,6 +400,7 @@ static const unsigned int a3xx_registers[] = { 0x303c, 0x303c, 0x305e, 0x305f, }; +#ifdef CONFIG_DEBUG_FS static void a3xx_show (struct msm_gpu *gpu,struct seq_file *m) {int i;@@ -415,6 +424,29 @@ static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m) }#endif +/* would be nice to not have to duplicate the _show() stuff with printk(): */ +static void a3xx_dump (struct msm_gpu *gpu) +{ +int i; + +adreno_dump (gpu); +printk ("status: %08x\n" , +gpu_read (gpu,REG_A3XX_RBBM_STATUS )); + +/* dump these out in a form that can be parsed by demsm: */ +printk ("IO:region %s 00000000 00020000\n" , gpu->name); +for (i =0 ; i <ARRAY_SIZE (a3xx_registers); i +=2 ) { +uint32_t start = a3xx_registers[i]; +uint32_t end = a3xx_registers[i+1 ]; +uint32_t addr; + +for (addr = start; addr <= end; addr++) { +uint32_t val =gpu_read (gpu, addr); +printk ("IO:R %08x %08x\n" , addr<<2 , val); + } + } +} +static const struct adreno_gpu_funcs funcs = { .base = { .get_param = adreno_get_param,drivers/gpu/drm/msm/adreno/adreno_gpu.c +8
-0@@ -260,6 +260,24 @@ void adreno_show(struct msm_gpu *gpu, struct seq_file *m) }#endif +/* would be nice to not have to duplicate the _show() stuff with printk(): */ +void adreno_dump (struct msm_gpu *gpu) +{ +struct adreno_gpu *adreno_gpu =to_adreno_gpu (gpu); + + printk("revision: %d (%d.%d.%d.%d)\n", + adreno_gpu->info->revn, adreno_gpu->rev.core, + adreno_gpu->rev.major, adreno_gpu->rev.minor,
drivers/gpu/drm/msm/adreno/adreno_gpu.c void adreno_show(struct msm_gpu *gpu, struct seq_file *m) · line 280
Quoted 10 lines
drivers/gpu/drm/msm/adreno/adreno_gpu.c +10
-0+ adreno_gpu->rev.patchid); + +printk ("fence: %d/%d\n" , adreno_gpu->memptrs->fence, + gpu->submitted_fence); +printk ("rptr: %d\n" , adreno_gpu->memptrs->rptr); +printk ("wptr: %d\n" , adreno_gpu->memptrs->wptr); +printk ("rb wptr: %d\n" ,get_wptr (gpu->rb)); + +} +
We should introduce you to the gooey wonderfulness of the snapshot :) All in due time.
Jordan
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH 2/7] drm/msm: add hang_debug module param
Quoted 49 lines · Jordan Crouse
Quoted 32 lines · Rob Clark
msm.hang_debug=y will dump out current register values if the gpu locks up, for easier debugging.
Signed-off-by Rob Clark <[email protected]>--- drivers/gpu/drm/msm/adreno/a3xx_gpu.c | 34 ++++++++++++++++++++++++++++++++- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 18 +++++++++++++++++ drivers/gpu/drm/msm/adreno/adreno_gpu.h | 1 + 3 files changed, 52 insertions(+), 1 deletion(-)drivers/gpu/drm/msm/adreno/a3xx_gpu.c +6
-0@@ -35,6 +35,12 @@ A3XX_INT0_CP_AHB_ERROR_HALT | \ A3XX_INT0_UCHE_OOB_ACCESS) + +static bool hang_debug = false; +MODULE_PARM_DESC(hang_debug, "Dump registers when hang is detected (can be slow!)"); +module_param_named(hang_debug, hang_debug, bool,0600 ); +static void a3xx_dump (struct msm_gpu *gpu); +static struct platform_device *a3xx_pdev;static void
a3xx_me_init(struct msm_gpu *gpu)drivers/gpu/drm/msm/adreno/a3xx_gpu.c static int a3xx_hw_init(struct msm_gpu *gpu) · line 302
Quoted 6 lines
drivers/gpu/drm/msm/adreno/a3xx_gpu.c +3
-0@@ -291,6 +297,9 @@ static int a3xx_hw_init(struct msm_gpu *gpu)static void a3xx_recover (struct msm_gpu *gpu) { +/* dump registers before resetting gpu, if enabled: */ +if (hang_debug) +a3xx_dump (gpu);You will need to be careful here - not all the registers are suitable for reading back.
On some flavors of A330 in particular some HLSQ registers reads will bring the system
down. You can see the gory details in the KGSL driver. If this works for you for now
great but fair warning that some cores might have different experiences.
Quoted 1 line
From what I've seen, certain types of GPU lockup (for example shader
const overrun), even on a320, leave things such that reading HLSQ registers does bad things, which is why I don't enable the bootarg by default.
If there is a way to *detect* when HLSQ is in a bad state and skip those registers, that would be awesome. I guess I should have another look at latest kgsl to see if there is some trick I missed.
Or otherwise I should probably make the bootarg a bitmask so you can choose to enable/disable certain groups of registers.
Quoted 78 lines
Quoted 78 lines
drivers/gpu/drm/msm/adreno/a3xx_gpu.c +2
-1gpu_write (gpu,REG_A3XX_RBBM_SW_RESET_CMD ,1 );gpu_read (gpu,REG_A3XX_RBBM_SW_RESET_CMD );gpu_write (gpu,REG_A3XX_RBBM_SW_RESET_CMD ,0 );@@ -352,7 +361,6 @@ static irqreturn_t a3xx_irq(struct msm_gpu *gpu)return IRQ_HANDLED ; } -#ifdef CONFIG_DEBUG_FS static const unsigned int a3xx_registers[] = {0x0000 ,0x0002 ,0x0010 ,0x0012 ,0x0018 ,0x0018 ,0x0020 ,0x0027 ,0x0029 ,0x002b ,0x002e ,0x0033 ,0x0040 ,0x0042 ,0x0050 ,0x005c ,@@ -392,6 +400,7 @@ static const unsigned int a3xx_registers[] = { 0x303c, 0x303c, 0x305e, 0x305f, }; +#ifdef CONFIG_DEBUG_FS static void a3xx_show (struct msm_gpu *gpu,struct seq_file *m) {int i;@@ -415,6 +424,29 @@ static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m) }#endif +/* would be nice to not have to duplicate the _show() stuff with
printk(): */drivers/gpu/drm/msm/adreno/a3xx_gpu.c +22
-0+static void a3xx_dump (struct msm_gpu *gpu) +{ +int i; + +adreno_dump (gpu); +printk ("status: %08x\n" , +gpu_read (gpu,REG_A3XX_RBBM_STATUS )); + +/* dump these out in a form that can be parsed by demsm: */ +printk ("IO:region %s 00000000 00020000\n" , gpu->name); +for (i =0 ; i <ARRAY_SIZE (a3xx_registers); i +=2 ) { +uint32_t start = a3xx_registers[i]; +uint32_t end = a3xx_registers[i+1 ]; +uint32_t addr; + +for (addr = start; addr <= end; addr++) { +uint32_t val =gpu_read (gpu, addr); +printk ("IO:R %08x %08x\n" , addr<<2 , val); + } + } +} +static const struct adreno_gpu_funcs funcs = { .base = { .get_param = adreno_get_param,drivers/gpu/drm/msm/adreno/adreno_gpu.c +1
-0@@ -260,6 +260,24 @@ void adreno_show(struct msm_gpu *gpu, struct seq_file *m) }#endif +/* would be nice to not have to duplicate the _show() stuff with
printk(): */drivers/gpu/drm/msm/adreno/adreno_gpu.c +14
-0+void adreno_dump (struct msm_gpu *gpu) +{ +struct adreno_gpu *adreno_gpu =to_adreno_gpu (gpu); + +printk ("revision: %d (%d.%d.%d.%d)\n" , + adreno_gpu->info->revn, adreno_gpu->rev.core, + adreno_gpu->rev.major, adreno_gpu->rev.minor, + adreno_gpu->rev.patchid); + +printk ("fence: %d/%d\n" , adreno_gpu->memptrs->fence, + gpu->submitted_fence); +printk ("rptr: %d\n" , adreno_gpu->memptrs->rptr); +printk ("wptr: %d\n" , adreno_gpu->memptrs->wptr); +printk ("rb wptr: %d\n" ,get_wptr (gpu->rb));
Quoted 8 lines
drivers/gpu/drm/msm/adreno/adreno_gpu.c
Quoted 3 lines
drivers/gpu/drm/msm/adreno/adreno_gpu.c +3
-0+ +} +We should introduce you to the gooey wonderfulness of the snapshot :) All in
due time.
if I get the rd dumping upstream (for which I first need to fix some chicken/egg problem in drm debugfs init), that plus scratch register writes (for tile and draw # so I can "triangulate" the position), it's actually not a bad way to debug.
(although I suppose there are some juicy status bits in the register dump which would be pretty helpful too if I knew what they meant :-P)
BR,
-R
Quoted 4 lines
Jordan
--
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH 3/7 drm/msm: spin helper
In reply to Rob ClarkHelper macro to simplify places where we need to poll with timeout waiting for gpu.
--- drivers/gpu/drm/msm/adreno/a3xx_gpu.c | 14 +++-------- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 41 ++++++++++++--------------------- drivers/gpu/drm/msm/adreno/adreno_gpu.h | 15 +++++++++++- 3 files changed, 32 insertions(+), 38 deletions(-)
drivers/gpu/drm/msm/adreno/a3xx_gpu.c +3 -11
@@ -326,21 +326,13 @@ static void a3xx_destroy(struct msm_gpu *gpu)static void a3xx_idle (struct msm_gpu *gpu) { -unsigned long t; -/* wait for ringbuffer to drain: */ adreno_idle (gpu); - t = jiffies +ADRENO_IDLE_TIMEOUT ; -/* then wait for GPU to finish: */ -do { -uint32_t rbbm_status =gpu_read (gpu,REG_A3XX_RBBM_STATUS ); -if (!(rbbm_status &A3XX_RBBM_STATUS_GPU_BUSY )) -return ; - }while (time_before (jiffies, t)); - -DRM_ERROR ("timeout waiting for %s to idle!\n" , gpu->name); +if (spin_until (!(gpu_read (gpu,REG_A3XX_RBBM_STATUS ) & +A3XX_RBBM_STATUS_GPU_BUSY ))) +DRM_ERROR ("%s: timeout waiting for GPU to idle!\n" , gpu->name);/* TODO maybe we need to reset GPU here to recover from hang? */ }
drivers/gpu/drm/msm/adreno/adreno_gpu.c +15 -26
56 lines not on this page · raw message
drivers/gpu/drm/msm/adreno/adreno_gpu.h +14 -1
@@ -76,7 +76,20 @@ struct adreno_platform_config {#endif }; -#define ADRENO_IDLE_TIMEOUT (20 * 1000) +#define ADRENO_IDLE_TIMEOUT msecs_to_jiffies(1000) + +#define spin_until (X ) ({ \ + int __ret = -ETIMEDOUT; \ + unsigned long __t = jiffies + ADRENO_IDLE_TIMEOUT; \ + do { \ + if (X) { \ + __ret = 0; \ + break; \ + } \ + } while (time_before(jiffies, __t)); \ + __ret; \ +}) +static inline bool adreno_is_a3xx (struct adreno_gpu *gpu) {
Signature · 1 line
1.8.5.3
Re: [PATCH 3/7] drm/msm: spin helper
Quoted 4 lines · Rob Clark
Helper macro to simplify places where we need to poll with timeout waiting for gpu.
Signed-off-by Rob Clark <[email protected]>
Quoted 124 lines
--- drivers/gpu/drm/msm/adreno/a3xx_gpu.c | 14 +++-------- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 41 ++++++++++++--------------------- drivers/gpu/drm/msm/adreno/adreno_gpu.h | 15 +++++++++++- 3 files changed, 32 insertions(+), 38 deletions(-)drivers/gpu/drm/msm/adreno/a3xx_gpu.c +3
-11@@ -326,21 +326,13 @@ static void a3xx_destroy(struct msm_gpu *gpu)static void a3xx_idle (struct msm_gpu *gpu) { -unsigned long t; -/* wait for ringbuffer to drain: */ adreno_idle (gpu); - t = jiffies +ADRENO_IDLE_TIMEOUT ; -/* then wait for GPU to finish: */ -do { -uint32_t rbbm_status =gpu_read (gpu,REG_A3XX_RBBM_STATUS ); -if (!(rbbm_status &A3XX_RBBM_STATUS_GPU_BUSY )) -return ; - }while (time_before (jiffies, t)); - -DRM_ERROR ("timeout waiting for %s to idle!\n" , gpu->name); +if (spin_until (!(gpu_read (gpu,REG_A3XX_RBBM_STATUS ) & +A3XX_RBBM_STATUS_GPU_BUSY ))) +DRM_ERROR ("%s: timeout waiting for GPU to idle!\n" , gpu->name);/* TODO maybe we need to reset GPU here to recover from hang? */ }drivers/gpu/drm/msm/adreno/adreno_gpu.c +15
-26@@ -225,19 +225,11 @@ void adreno_flush(struct msm_gpu *gpu)void adreno_idle (struct msm_gpu *gpu) {struct adreno_gpu *adreno_gpu =to_adreno_gpu (gpu); -uint32_t rptr, wptr =get_wptr (gpu->rb); -unsigned long t; - - t = jiffies +ADRENO_IDLE_TIMEOUT ; - -/* then wait for CP to drain ringbuffer: */ -do { - rptr = adreno_gpu->memptrs->rptr; -if (rptr == wptr) -return ; - }while (time_before (jiffies, t)); +uint32_t wptr =get_wptr (gpu->rb); -DRM_ERROR ("%s: timeout waiting to drain ringbuffer!\n" , gpu->name); +/* wait for CP to drain ringbuffer: */ +if (spin_until (adreno_gpu->memptrs->rptr == wptr)) +DRM_ERROR ("%s: timeout waiting to drain ringbuffer!\n" , gpu->name);/* TODO maybe we need to reset GPU here to recover from hang? */ }@@ -278,22 +270,19 @@ void adreno_dump(struct msm_gpu *gpu) } -void adreno_wait_ring (struct msm_gpu *gpu,uint32_t ndwords) +static uint32_t ring_freewords (struct msm_gpu *gpu) {struct adreno_gpu *adreno_gpu =to_adreno_gpu (gpu); -uint32_t freedwords; -unsigned long t = jiffies +ADRENO_IDLE_TIMEOUT ; -do { -uint32_t size = gpu->rb->size /4 ; -uint32_t wptr =get_wptr (gpu->rb); -uint32_t rptr = adreno_gpu->memptrs->rptr; - freedwords = (rptr + (size -1 ) - wptr) % size; - -if (time_after (jiffies, t)) { -DRM_ERROR ("%s: timeout waiting for ringbuffer space\n" , gpu->name); -break ; - } - }while (freedwords < ndwords); +uint32_t size = gpu->rb->size /4 ; +uint32_t wptr =get_wptr (gpu->rb); +uint32_t rptr = adreno_gpu->memptrs->rptr; +return (rptr + (size -1 ) - wptr) % size; +} + +void adreno_wait_ring (struct msm_gpu *gpu,uint32_t ndwords) +{ +if (spin_until (ring_freewords (gpu) >= ndwords)) +DRM_ERROR ("%s: timeout waiting for ringbuffer space\n" , gpu->name); }static const char *iommu_ports[] = {drivers/gpu/drm/msm/adreno/adreno_gpu.h +14
-1@@ -76,7 +76,20 @@ struct adreno_platform_config {#endif }; -#define ADRENO_IDLE_TIMEOUT (20 * 1000) +#define ADRENO_IDLE_TIMEOUT msecs_to_jiffies(1000) + +#define spin_until (X ) ({ \ + int __ret = -ETIMEDOUT; \ + unsigned long __t = jiffies + ADRENO_IDLE_TIMEOUT; \ + do { \ + if (X) { \ + __ret = 0; \ + break; \ + } \ + } while (time_before(jiffies, __t)); \ + __ret; \ +}) +static inline bool adreno_is_a3xx (struct adreno_gpu *gpu) {
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH 4/7 drm/msm: crank down gpu when inactive
In reply to Rob ClarkShut down the clks when the gpu has nothing to do. A short inactivity timer is used to provide a low pass filter for power transitions.
--- drivers/gpu/drm/msm/adreno/a3xx_gpu.c | 10 +++++ drivers/gpu/drm/msm/msm_drv.c | 7 ++- drivers/gpu/drm/msm/msm_gpu.c | 85 +++++++++++++++++++++++++++++++++-- drivers/gpu/drm/msm/msm_gpu.h | 16 ++++++- 4 files changed, 113 insertions(+), 5 deletions(-)
drivers/gpu/drm/msm/adreno/a3xx_gpu.c +10 -0
@@ -395,9 +395,15 @@ static const unsigned int a3xx_registers[] = {#ifdef CONFIG_DEBUG_FS static void a3xx_show (struct msm_gpu *gpu,struct seq_file *m) { +struct drm_device *dev = gpu->dev;int i;adreno_show (gpu, m); + +mutex_lock (&dev->struct_mutex); + + gpu->funcs->pm_resume (gpu); +seq_printf (m,"status: %08x\n" ,gpu_read (gpu,REG_A3XX_RBBM_STATUS ));
@@ -413,6 +419,10 @@ static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m) seq_printf(m, "IO:R %08x %08x\n", addr<<2, val); } } + + gpu->funcs->pm_suspend (gpu); + +mutex_unlock (&dev->struct_mutex); }#endif
drivers/gpu/drm/msm/msm_drv.c +6 -1
@@ -311,7 +311,6 @@ static void load_gpu(struct drm_device *dev) gpu =NULL ;/* not fatal */ } -mutex_unlock (&dev->struct_mutex);if (gpu) {int ret;
@@ -321,10 +320,16 @@ static void load_gpu(struct drm_device *dev)dev_err (dev->dev,"gpu hw init failed: %d\n" , ret); gpu->funcs->destroy (gpu); gpu =NULL ; + }else { +/* give inactive pm a chance to kick in: */ +msm_gpu_retire (gpu); } + } priv->gpu = gpu; + +mutex_unlock (&dev->struct_mutex); }static int msm_open (struct drm_device *dev,struct drm_file *file)
drivers/gpu/drm/msm/msm_gpu.c +82 -3
136 lines not on this page · raw message
drivers/gpu/drm/msm/msm_gpu.h +15 -1
@@ -72,6 +72,10 @@ struct msm_gpu {uint32_t submitted_fence; +/* is gpu powered/active? */ +int active_cnt; +bool inactive; +/* worker for handling active-list retiring: */ struct work_struct retire_work;
@@ -91,7 +95,12 @@ struct msm_gpu {uint32_t bsc;#endif -/* Hang Detction: */ +/* Hang and Inactivity Detection: +*/ +#define DRM_MSM_INACTIVE_PERIOD 66/* in ms (roughly four frames) */ +#define DRM_MSM_INACTIVE_JIFFIES msecs_to_jiffies(DRM_MSM_INACTIVE_PERIOD) +struct timer_list inactive_timer; +struct work_struct inactive_work;#define DRM_MSM_HANGCHECK_PERIOD 500/* in ms */ #define DRM_MSM_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_MSM_HANGCHECK_PERIOD)struct timer_list hangcheck_timer;
@@ -99,6 +108,11 @@ struct msm_gpu {struct work_struct recover_work; }; +static inline bool msm_gpu_active (struct msm_gpu *gpu) +{ +return gpu->submitted_fence > gpu->funcs->last_fence (gpu); +} +static inline void gpu_write (struct msm_gpu *gpu,u32 reg,u32 data) {msm_writel (data, gpu->mmio + (reg <<2 ));
Signature · 1 line
1.8.5.3
Re: [PATCH 4/7] drm/msm: crank down gpu when inactive
Quoted 2 lines · Rob Clark
Shut down the clks when the gpu has nothing to do. A short inactivity timer is used to provide a low pass filter for power transitions.
Good luck. Power management will take years off your life.
Quoted 1 line
Signed-off-by Rob Clark <[email protected]>
Quoted 256 lines
--- drivers/gpu/drm/msm/adreno/a3xx_gpu.c | 10 +++++ drivers/gpu/drm/msm/msm_drv.c | 7 ++- drivers/gpu/drm/msm/msm_gpu.c | 85 +++++++++++++++++++++++++++++++++-- drivers/gpu/drm/msm/msm_gpu.h | 16 ++++++- 4 files changed, 113 insertions(+), 5 deletions(-)drivers/gpu/drm/msm/adreno/a3xx_gpu.c +10
-0@@ -395,9 +395,15 @@ static const unsigned int a3xx_registers[] = {#ifdef CONFIG_DEBUG_FS static void a3xx_show (struct msm_gpu *gpu,struct seq_file *m) { +struct drm_device *dev = gpu->dev;int i;adreno_show (gpu, m); + +mutex_lock (&dev->struct_mutex); + + gpu->funcs->pm_resume (gpu); +seq_printf (m,"status: %08x\n" ,gpu_read (gpu,REG_A3XX_RBBM_STATUS ));@@ -413,6 +419,10 @@ static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m) seq_printf(m, "IO:R %08x %08x\n", addr<<2, val); } } + + gpu->funcs->pm_suspend (gpu); + +mutex_unlock (&dev->struct_mutex); }#endif drivers/gpu/drm/msm/msm_drv.c +6
-1@@ -311,7 +311,6 @@ static void load_gpu(struct drm_device *dev) gpu =NULL ;/* not fatal */ } -mutex_unlock (&dev->struct_mutex);if (gpu) {int ret;@@ -321,10 +320,16 @@ static void load_gpu(struct drm_device *dev)dev_err (dev->dev,"gpu hw init failed: %d\n" , ret); gpu->funcs->destroy (gpu); gpu =NULL ; + }else { +/* give inactive pm a chance to kick in: */ +msm_gpu_retire (gpu); } + } priv->gpu = gpu; + +mutex_unlock (&dev->struct_mutex); }static int msm_open (struct drm_device *dev,struct drm_file *file)drivers/gpu/drm/msm/msm_gpu.c +82
-3@@ -154,9 +154,18 @@ static int disable_axi(struct msm_gpu *gpu)int msm_gpu_pm_resume (struct msm_gpu *gpu) { +struct drm_device *dev = gpu->dev;int ret; -DBG ("%s" , gpu->name); +DBG ("%s: active_cnt=%d" , gpu->name, gpu->active_cnt); + +WARN_ON (!mutex_is_locked (&dev->struct_mutex)); + +if (gpu->active_cnt++ >0 ) +return 0 ; + +if (WARN_ON (gpu->active_cnt <=0 )) +return -EINVAL ; ret =enable_pwrrail (gpu);if (ret)@@ -175,9 +184,18 @@ int msm_gpu_pm_resume(struct msm_gpu *gpu)int msm_gpu_pm_suspend (struct msm_gpu *gpu) { +struct drm_device *dev = gpu->dev;int ret; -DBG ("%s" , gpu->name); +DBG ("%s: active_cnt=%d" , gpu->name, gpu->active_cnt); + +WARN_ON (!mutex_is_locked (&dev->struct_mutex)); + +if (--gpu->active_cnt >0 ) +return 0 ; + +if (WARN_ON (gpu->active_cnt <0 )) +return -EINVAL ; ret =disable_axi (gpu);if (ret)@@ -195,6 +213,55 @@ int msm_gpu_pm_suspend(struct msm_gpu *gpu) }/* +* Inactivity detection (for suspend): +*/ + +static void inactive_worker (struct work_struct *work) +{ + struct msm_gpu *gpu = container_of(work, struct msm_gpu, inactive_work); +struct drm_device *dev = gpu->dev; + +if (gpu->inactive) +return ; + +DBG ("%s: inactive!\n" , gpu->name); +mutex_lock (&dev->struct_mutex); +if (!(msm_gpu_active (gpu) || gpu->inactive)) { +disable_axi (gpu); +disable_clk (gpu); + gpu->inactive = true; + } +mutex_unlock (&dev->struct_mutex); +} + +static void inactive_handler (unsigned long data) +{ +struct msm_gpu *gpu = (struct msm_gpu *)data; +struct msm_drm_private *priv = gpu->dev->dev_private; + +queue_work (priv->wq, &gpu->inactive_work); +} + +/* cancel inactive timer and make sure we are awake: */ +static void inactive_cancel (struct msm_gpu *gpu) +{ +DBG ("%s" , gpu->name); +del_timer (&gpu->inactive_timer); +if (gpu->inactive) { +enable_clk (gpu); +enable_axi (gpu); + gpu->inactive = false; + } +} + +static void inactive_start (struct msm_gpu *gpu) +{ +DBG ("%s" , gpu->name); +mod_timer (&gpu->inactive_timer, +round_jiffies_up (jiffies +DRM_MSM_INACTIVE_JIFFIES )); +} + +/* * Hangcheck detection for locked gpu: */ @@ -206,7 +273,10 @@ static void recover_worker(struct work_struct *work)dev_err (dev->dev,"%s: hangcheck recover!\n" , gpu->name);mutex_lock (&dev->struct_mutex); - gpu->funcs->recover (gpu); +if (msm_gpu_active (gpu)) { +inactive_cancel (gpu); + gpu->funcs->recover (gpu); + }mutex_unlock (&dev->struct_mutex);msm_gpu_retire (gpu);@@ -281,6 +351,9 @@ static void retire_worker(struct work_struct *work) }mutex_unlock (&dev->struct_mutex); + +if (!msm_gpu_active (gpu)) +inactive_start (gpu); }/* call from irq handler to schedule work to retire bo's */ @@ -302,6 +375,8 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, gpu->submitted_fence = submit->fence; +inactive_cancel (gpu); + ret = gpu->funcs->submit (gpu, submit, ctx); priv->lastctx = ctx;@@ -357,11 +432,15 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, gpu->dev = drm; gpu->funcs = funcs; gpu->name = name; + gpu->inactive = true;INIT_LIST_HEAD (&gpu->active_list);INIT_WORK (&gpu->retire_work, retire_worker); +INIT_WORK (&gpu->inactive_work, inactive_worker);INIT_WORK (&gpu->recover_work, recover_worker); +setup_timer (&gpu->inactive_timer, inactive_handler, + (unsigned long )gpu);setup_timer (&gpu->hangcheck_timer, hangcheck_handler, (unsigned long )gpu);drivers/gpu/drm/msm/msm_gpu.h +15
-1@@ -72,6 +72,10 @@ struct msm_gpu {uint32_t submitted_fence; +/* is gpu powered/active? */ +int active_cnt; +bool inactive; +/* worker for handling active-list retiring: */ struct work_struct retire_work;@@ -91,7 +95,12 @@ struct msm_gpu {uint32_t bsc;#endif -/* Hang Detction: */ +/* Hang and Inactivity Detection: +*/ +#define DRM_MSM_INACTIVE_PERIOD 66/* in ms (roughly four frames) */ +#define DRM_MSM_INACTIVE_JIFFIES msecs_to_jiffies(DRM_MSM_INACTIVE_PERIOD) +struct timer_list inactive_timer; +struct work_struct inactive_work;#define DRM_MSM_HANGCHECK_PERIOD 500/* in ms */ #define DRM_MSM_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_MSM_HANGCHECK_PERIOD)struct timer_list hangcheck_timer;@@ -99,6 +108,11 @@ struct msm_gpu {struct work_struct recover_work; }; +static inline bool msm_gpu_active (struct msm_gpu *gpu) +{ +return gpu->submitted_fence > gpu->funcs->last_fence (gpu); +} +static inline void gpu_write (struct msm_gpu *gpu,u32 reg,u32 data) {msm_writel (data, gpu->mmio + (reg <<2 ));
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH 4/7] drm/msm: crank down gpu when inactive
Quoted 7 lines · Jordan Crouse
Quoted 3 lines · Rob Clark
Shut down the clks when the gpu has nothing to do. A short inactivity timer is used to provide a low pass filter for power transitions.
Good luck. Power management will take years off your life.
that is why I started with something extremely simple ;-)
I've actually had this patch right around the time of the 3.14 merge window, but didn't want to push this sort of thing in last minute. I've been using it locally since then, and so far so good
BR,
-R
Quoted 277 lines
Quoted 2 lines
Signed-off-by Rob Clark <[email protected]>Acked-by Jordan Crouse <[email protected]>Quoted 256 lines
--- drivers/gpu/drm/msm/adreno/a3xx_gpu.c | 10 +++++ drivers/gpu/drm/msm/msm_drv.c | 7 ++- drivers/gpu/drm/msm/msm_gpu.c | 85 +++++++++++++++++++++++++++++++++-- drivers/gpu/drm/msm/msm_gpu.h | 16 ++++++- 4 files changed, 113 insertions(+), 5 deletions(-)drivers/gpu/drm/msm/adreno/a3xx_gpu.c +10
-0@@ -395,9 +395,15 @@ static const unsigned int a3xx_registers[] = {#ifdef CONFIG_DEBUG_FS static void a3xx_show (struct msm_gpu *gpu,struct seq_file *m) { +struct drm_device *dev = gpu->dev;int i;adreno_show (gpu, m); + +mutex_lock (&dev->struct_mutex); + + gpu->funcs->pm_resume (gpu); +seq_printf (m,"status: %08x\n" ,gpu_read (gpu,REG_A3XX_RBBM_STATUS ));@@ -413,6 +419,10 @@ static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m) seq_printf(m, "IO:R %08x %08x\n", addr<<2, val); } } + + gpu->funcs->pm_suspend (gpu); + +mutex_unlock (&dev->struct_mutex); }#endif drivers/gpu/drm/msm/msm_drv.c +6
-1@@ -311,7 +311,6 @@ static void load_gpu(struct drm_device *dev) gpu =NULL ;/* not fatal */ } -mutex_unlock (&dev->struct_mutex);if (gpu) {int ret;@@ -321,10 +320,16 @@ static void load_gpu(struct drm_device *dev)dev_err (dev->dev,"gpu hw init failed: %d\n" , ret); gpu->funcs->destroy (gpu); gpu =NULL ; + }else { +/* give inactive pm a chance to kick in: */ +msm_gpu_retire (gpu); } + } priv->gpu = gpu; + +mutex_unlock (&dev->struct_mutex); }static int
msm_open(struct drm_device *dev, struct drm_file *file)drivers/gpu/drm/msm/msm_gpu.c +69
-2@@ -154,9 +154,18 @@ static int disable_axi(struct msm_gpu *gpu)int msm_gpu_pm_resume (struct msm_gpu *gpu) { +struct drm_device *dev = gpu->dev;int ret; -DBG ("%s" , gpu->name); +DBG ("%s: active_cnt=%d" , gpu->name, gpu->active_cnt); + +WARN_ON (!mutex_is_locked (&dev->struct_mutex)); + +if (gpu->active_cnt++ >0 ) +return 0 ; + +if (WARN_ON (gpu->active_cnt <=0 )) +return -EINVAL ; ret =enable_pwrrail (gpu);if (ret)@@ -175,9 +184,18 @@ int msm_gpu_pm_resume(struct msm_gpu *gpu)int msm_gpu_pm_suspend (struct msm_gpu *gpu) { +struct drm_device *dev = gpu->dev;int ret; -DBG ("%s" , gpu->name); +DBG ("%s: active_cnt=%d" , gpu->name, gpu->active_cnt); + +WARN_ON (!mutex_is_locked (&dev->struct_mutex)); + +if (--gpu->active_cnt >0 ) +return 0 ; + +if (WARN_ON (gpu->active_cnt <0 )) +return -EINVAL ; ret =disable_axi (gpu);if (ret)@@ -195,6 +213,55 @@ int msm_gpu_pm_suspend(struct msm_gpu *gpu) }/* +* Inactivity detection (for suspend): +*/ + +static void inactive_worker (struct work_struct *work) +{ + struct msm_gpu *gpu = container_of(work, struct msm_gpu, inactive_work); +struct drm_device *dev = gpu->dev; + +if (gpu->inactive) +return ; + +DBG ("%s: inactive!\n" , gpu->name); +mutex_lock (&dev->struct_mutex); +if (!(msm_gpu_active (gpu) || gpu->inactive)) { +disable_axi (gpu); +disable_clk (gpu); + gpu->inactive = true; + } +mutex_unlock (&dev->struct_mutex); +} + +static void inactive_handler (unsigned long data) +{ +struct msm_gpu *gpu = (struct msm_gpu *)data; +struct msm_drm_private *priv = gpu->dev->dev_private; + +queue_work (priv->wq, &gpu->inactive_work); +} + +/* cancel inactive timer and make sure we are awake: */ +static void inactive_cancel (struct msm_gpu *gpu) +{ +DBG ("%s" , gpu->name); +del_timer (&gpu->inactive_timer); +if (gpu->inactive) { +enable_clk (gpu); +enable_axi (gpu); + gpu->inactive = false; + } +} + +static void inactive_start (struct msm_gpu *gpu) +{ +DBG ("%s" , gpu->name); +mod_timer (&gpu->inactive_timer, +round_jiffies_up (jiffies +DRM_MSM_INACTIVE_JIFFIES )); +} + +/* * Hangcheck detection for locked gpu: */drivers/gpu/drm/msm/msm_gpu.c +13
-1@@ -206,7 +273,10 @@ static void recover_worker(struct work_struct *work)dev_err (dev->dev,"%s: hangcheck recover!\n" , gpu->name);mutex_lock (&dev->struct_mutex); - gpu->funcs->recover (gpu); +if (msm_gpu_active (gpu)) { +inactive_cancel (gpu); + gpu->funcs->recover (gpu); + }mutex_unlock (&dev->struct_mutex);msm_gpu_retire (gpu);@@ -281,6 +351,9 @@ static void retire_worker(struct work_struct *work) }mutex_unlock (&dev->struct_mutex); + +if (!msm_gpu_active (gpu)) +inactive_start (gpu); }/* call from irq handler to schedule work to retire bo's */ @@ -302,6 +375,8 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, gpu->submitted_fence = submit->fence; +inactive_cancel (gpu); + ret = gpu->funcs->submit (gpu, submit, ctx); priv->lastctx = ctx;@@ -357,11 +432,15 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, gpu->dev = drm; gpu->funcs = funcs; gpu->name = name; + gpu->inactive = true;INIT_LIST_HEAD (&gpu->active_list);INIT_WORK (&gpu->retire_work, retire_worker); +INIT_WORK (&gpu->inactive_work, inactive_worker);INIT_WORK (&gpu->recover_work, recover_worker); +setup_timer (&gpu->inactive_timer, inactive_handler, + (unsigned long )gpu);setup_timer (&gpu->hangcheck_timer, hangcheck_handler, (unsigned long )gpu);drivers/gpu/drm/msm/msm_gpu.h +10
-1@@ -72,6 +72,10 @@ struct msm_gpu {uint32_t submitted_fence; +/* is gpu powered/active? */ +int active_cnt; +bool inactive; +/* worker for handling active-list retiring: */ struct work_struct retire_work;@@ -91,7 +95,12 @@ struct msm_gpu {uint32_t bsc;#endif -/* Hang Detction: */ +/* Hang and Inactivity Detection: +*/ +#define DRM_MSM_INACTIVE_PERIOD 66/* in ms (roughly four frames) */ +#define DRM_MSM_INACTIVE_JIFFIES msecs_to_jiffies (DRM_MSM_INACTIVE_PERIOD) + struct timer_list inactive_timer; +struct work_struct inactive_work;#define DRM_MSM_HANGCHECK_PERIOD 500/* in ms */ #define DRM_MSM_HANGCHECK_JIFFIES
msecs_to_jiffies(DRM_MSM_HANGCHECK_PERIOD)struct timer_list hangcheck_timer;drivers/gpu/drm/msm/msm_gpu.h +0
-0@@ -99,6 +108,11 @@ struct msm_gpu {struct work_struct recover_work;drivers/gpu/drm/msm/msm_gpu.h struct msm_gpu { · line 118
Quoted 10 lines
drivers/gpu/drm/msm/msm_gpu.h +5
-0}; +static inline bool msm_gpu_active (struct msm_gpu *gpu) +{ +return gpu->submitted_fence > gpu->funcs->last_fence (gpu); +} +static inline void gpu_write (struct msm_gpu *gpu,u32 reg,u32 data) {msm_writel (data, gpu->mmio + (reg <<2 ));--
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH 5/7 drm/msm: add chip-id param
In reply to Rob ClarkSome of the w/a or different behavior of userspace blob driver seem to be keyed to gpu patch revision, rather than gpu-id. So expose the full chip-id to userspace so it can DTRT.
--- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 6 ++++++ include/uapi/drm/msm_drm.h | 1 + 2 files changed, 7 insertions(+)
drivers/gpu/drm/msm/adreno/adreno_gpu.c +6 -0
@@ -73,6 +73,12 @@ int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value)case MSM_PARAM_GMEM_SIZE : *value = adreno_gpu->gmem;return 0 ; +case MSM_PARAM_CHIP_ID : + *value = adreno_gpu->rev.patchid | + (adreno_gpu->rev.minor <<8 ) | + (adreno_gpu->rev.major <<16 ) | + (adreno_gpu->rev.core <<24 ); +return 0 ;default :DBG ("%s: invalid param: %u" , gpu->name, param);return -EINVAL ;
include/uapi/drm/msm_drm.h +1 -0
@@ -50,6 +50,7 @@ struct drm_msm_timespec {#define MSM_PARAM_GPU_ID 0x01#define MSM_PARAM_GMEM_SIZE 0x02 +#define MSM_PARAM_CHIP_ID 0x03struct drm_msm_param {uint32_t pipe;/* in, MSM_PIPE_x */
Signature · 1 line
1.8.5.3
Re: [PATCH 5/7] drm/msm: add chip-id param
Quoted 3 lines · Rob Clark
Some of the w/a or different behavior of userspace blob driver seem to be keyed to gpu patch revision, rather than gpu-id. So expose the full chip-id to userspace so it can DTRT.
It is all chip-id all the time in the blob - we used to try to be clever with unique GPU IDs and such with it but we discovered over time that there is just too much drama.
Quoted 1 line
Signed-off-by Rob Clark <[email protected]>
Quoted 35 lines
--- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 6 ++++++ include/uapi/drm/msm_drm.h | 1 + 2 files changed, 7 insertions(+)drivers/gpu/drm/msm/adreno/adreno_gpu.c +6
-0@@ -73,6 +73,12 @@ int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value)case MSM_PARAM_GMEM_SIZE : *value = adreno_gpu->gmem;return 0 ; +case MSM_PARAM_CHIP_ID : + *value = adreno_gpu->rev.patchid | + (adreno_gpu->rev.minor <<8 ) | + (adreno_gpu->rev.major <<16 ) | + (adreno_gpu->rev.core <<24 ); +return 0 ;default :DBG ("%s: invalid param: %u" , gpu->name, param);return -EINVAL ;include/uapi/drm/msm_drm.h +1
-0@@ -50,6 +50,7 @@ struct drm_msm_timespec {#define MSM_PARAM_GPU_ID 0x01#define MSM_PARAM_GMEM_SIZE 0x02 +#define MSM_PARAM_CHIP_ID 0x03struct drm_msm_param {uint32_t pipe;/* in, MSM_PIPE_x */
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH 6/7 drm/msm: use componentised device support
In reply to Rob Clark--- drivers/gpu/drm/msm/adreno/a3xx_gpu.c | 48 ++++++++---- drivers/gpu/drm/msm/hdmi/hdmi.c | 43 +++++++---- drivers/gpu/drm/msm/msm_drv.c | 133 +++++++++++++++++++++++++++++++++- drivers/gpu/drm/msm/msm_drv.h | 4 + 4 files changed, 197 insertions(+), 31 deletions(-)
drivers/gpu/drm/msm/adreno/a3xx_gpu.c +34 -14
@@ -41,8 +41,6 @@ MODULE_PARM_DESC(hang_debug, "Dump registers when hang is detected (can be slow!module_param_named (hang_debug, hang_debug, bool,0600 );static void a3xx_dump (struct msm_gpu *gpu); -static struct platform_device *a3xx_pdev; -static void a3xx_me_init (struct msm_gpu *gpu) {struct msm_ringbuffer *ring = gpu->rb;
@@ -320,7 +318,6 @@ static void a3xx_destroy(struct msm_gpu *gpu)ocmem_free (OCMEM_GRAPHICS , a3xx_gpu->ocmem_hdl);#endif -put_device (&a3xx_gpu->pdev->dev);kfree (a3xx_gpu); }
@@ -473,7 +470,8 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)struct a3xx_gpu *a3xx_gpu =NULL ;struct adreno_gpu *adreno_gpu;struct msm_gpu *gpu; -struct platform_device *pdev = a3xx_pdev; +struct msm_drm_private *priv = dev->dev_private; +struct platform_device *pdev = priv->gpu_pdev;struct adreno_platform_config *config;int ret;
@@ -494,7 +492,6 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev) adreno_gpu = &a3xx_gpu->base; gpu = &adreno_gpu->base; -get_device (&pdev->dev); a3xx_gpu->pdev = pdev; gpu->fast_rate = config->fast_rate;
@@ -556,17 +553,24 @@ fail:# include <mach/kgsl.h> #endif -static int a3xx_probe (struct platform_device *pdev) +static void set_gpu_pdev (struct drm_device *dev, +struct platform_device *pdev) +{ +struct msm_drm_private *priv = dev->dev_private; + priv->gpu_pdev = pdev; +} + +static int a3xx_bind (struct device *dev,struct device *master,void *data) {static struct adreno_platform_config config = {};#ifdef CONFIG_OF -struct device_node *child, *node = pdev->dev.of_node; +struct device_node *child, *node = dev->of_node;u32 val;int ret; ret =of_property_read_u32 (node,"qcom,chipid" , &val);if (ret) { -dev_err (&pdev->dev,"could not find chipid: %d\n" , ret); +dev_err (dev,"could not find chipid: %d\n" , ret);return ret; }
@@ -582,7 +586,7 @@ static int a3xx_probe(struct platform_device *pdev)for_each_child_of_node (child, pwrlvl) { ret =of_property_read_u32 (pwrlvl,"qcom,gpu-freq" , &val);if (ret) { -dev_err (&pdev->dev,"could not find gpu-freq: %d\n" , ret); +dev_err (dev,"could not find gpu-freq: %d\n" , ret);return ret; } config.fast_rate =max (config.fast_rate, val);
@@ -592,12 +596,12 @@ static int a3xx_probe(struct platform_device *pdev) }if (!config.fast_rate) { -dev_err (&pdev->dev,"could not find clk rates\n" ); +dev_err (dev,"could not find clk rates\n" );return -ENXIO ; }#else -struct kgsl_device_platform_data *pdata = pdev->dev.platform_data; +struct kgsl_device_platform_data *pdata = dev->platform_data;uint32_t version =socinfo_get_version ();if (cpu_is_apq8064ab ()) { config.fast_rate =450000000 ;
@@ -643,14 +647,30 @@ static int a3xx_probe(struct platform_device *pdev) config.bus_scale_table = pdata->bus_scale_table;# endif #endif - pdev->dev.platform_data = &config; - a3xx_pdev = pdev; + dev->platform_data = &config; +set_gpu_pdev (dev_get_drvdata (master),to_platform_device (dev));return 0 ; } +static void a3xx_unbind (struct device *dev,struct device *master, +void *data) +{ +set_gpu_pdev (dev_get_drvdata (master),NULL ); +} + +static const struct component_ops a3xx_ops = { + .bind = a3xx_bind, + .unbind = a3xx_unbind, +}; + +static int a3xx_probe (struct platform_device *pdev) +{ +return component_add (&pdev->dev, &a3xx_ops); +} +static int a3xx_remove (struct platform_device *pdev) { - a3xx_pdev =NULL ; +component_del (&pdev->dev, &a3xx_ops);return 0 ; }
drivers/gpu/drm/msm/hdmi/hdmi.c +30 -13
92 lines not on this page · raw message
drivers/gpu/drm/msm/msm_drv.c +129 -4
179 lines not on this page · raw message
drivers/gpu/drm/msm/msm_drv.h +4 -0
@@ -22,6 +22,7 @@#include <linux/clk.h> #include <linux/cpufreq.h> #include <linux/module.h> +#include <linux/component.h> #include <linux/platform_device.h> #include <linux/pm.h> #include <linux/pm_runtime.h>
@@ -69,6 +70,9 @@ struct msm_drm_private {struct msm_kms *kms; +/* subordinate devices, if present: */ +struct platform_device *hdmi_pdev, *gpu_pdev; +/* when we have more than one 'msm_gpu' these need to be an array: */ struct msm_gpu *gpu;struct msm_file_private *lastctx;
Signature · 1 line
1.8.5.3
PATCH 7/7 drm/msm: validate flags, etc
In reply to Rob ClarkAfter reading a nice article on LWN[1], I went back and double checked my handling of invalid-input checking. Turns out there were a couple places I had missed.
Since the driver is fairly young, and the devices it supports are really only just barely usable for basic stuff (serial console) with an upstream kernel, I think we should fix this now and revert specific parts of this patch later in the unlikely event that a regression is reported.
[1] https://lwn.net/Articles/588444/
--- drivers/gpu/drm/msm/msm_drv.c | 20 +++++++++++++++++++- drivers/gpu/drm/msm/msm_gem_submit.c | 15 +++++++++++++-- include/uapi/drm/msm_drm.h | 11 +++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-)
drivers/gpu/drm/msm/msm_drv.c +19 -1
@@ -664,6 +664,12 @@ static int msm_ioctl_gem_new(struct drm_device *dev, void *data, struct drm_file *file) {struct drm_msm_gem_new *args = data; + +if (args->flags & ~MSM_BO_FLAGS ) { +DRM_ERROR ("invalid flags: %08x\n" , args->flags); +return -EINVAL ; + } +return msm_gem_new_handle (dev, file, args->size, args->flags, &args->handle); }
@@ -677,6 +683,11 @@ static int msm_ioctl_gem_cpu_prep(struct drm_device *dev, void *data,struct drm_gem_object *obj;int ret; +if (args->op & ~MSM_PREP_FLAGS ) { +DRM_ERROR ("invalid op: %08x\n" , args->op); +return -EINVAL ; + } + obj =drm_gem_object_lookup (dev, file, args->handle);if (!obj)return -ENOENT ;
@@ -731,7 +742,14 @@ static int msm_ioctl_wait_fence(struct drm_device *dev, void *data, struct drm_file *file) {struct drm_msm_wait_fence *args = data; -return msm_wait_fence_interruptable (dev, args->fence, &TS (args->timeout)); + +if (args->pad) { +DRM_ERROR ("invalid pad: %08x\n" , args->pad); +return -EINVAL ; + } + +return msm_wait_fence_interruptable (dev, args->fence, + &TS (args->timeout)); }static const struct drm_ioctl_desc msm_ioctls[] = {
drivers/gpu/drm/msm/msm_gem_submit.c +13 -2
@@ -23,7 +23,6 @@* Cmdstream submission: */ -#define BO_INVALID_FLAGS ~(MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE)/* make sure these don't conflict w/ MSM_SUBMIT_BO_x */ #define BO_VALID 0x8000#define BO_LOCKED 0x4000
@@ -77,7 +76,7 @@ static int submit_lookup_objects(struct msm_gem_submit *submit, goto out_unlock; } -if (submit_bo.flags &BO_INVALID_FLAGS ) { +if (submit_bo.flags & ~MSM_SUBMIT_BO_FLAGS ) {DRM_ERROR ("invalid flags: %x\n" , submit_bo.flags); ret = -EINVAL ; goto out_unlock;
@@ -369,6 +368,18 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, goto out; } +/* validate input from userspace: */ +switch (submit_cmd.type) { +case MSM_SUBMIT_CMD_BUF : +case MSM_SUBMIT_CMD_IB_TARGET_BUF : +case MSM_SUBMIT_CMD_CTX_RESTORE_BUF : +break ; +default : +DRM_ERROR ("invalid type: %08x\n" , submit_cmd.type); + ret = -EINVAL ; + goto out; + } + ret =submit_bo (submit, submit_cmd.submit_idx, &msm_obj, &iova,NULL );if (ret)
include/uapi/drm/msm_drm.h +11 -0
@@ -70,6 +70,12 @@ struct drm_msm_param {#define MSM_BO_WC 0x00020000#define MSM_BO_UNCACHED 0x00040000 +#define MSM_BO_FLAGS (MSM_BO_SCANOUT | \ + MSM_BO_GPU_READONLY | \ + MSM_BO_CACHED | \ + MSM_BO_WC | \ + MSM_BO_UNCACHED) +struct drm_msm_gem_new {uint64_t size;/* in */ uint32_t flags;/* in, mask of MSM_BO_x */
@@ -86,6 +92,8 @@ struct drm_msm_gem_info {#define MSM_PREP_WRITE 0x02#define MSM_PREP_NOSYNC 0x04 +#define MSM_PREP_FLAGS (MSM_PREP_READ | MSM_PREP_WRITE | MSM_PREP_NOSYNC) +struct drm_msm_gem_cpu_prep {uint32_t handle;/* in */ uint32_t op;/* in, mask of MSM_PREP_x */
@@ -153,6 +161,9 @@ struct drm_msm_gem_submit_cmd {*/ #define MSM_SUBMIT_BO_READ 0x0001#define MSM_SUBMIT_BO_WRITE 0x0002 + +#define MSM_SUBMIT_BO_FLAGS (MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE) +struct drm_msm_gem_submit_bo {uint32_t flags;/* in, mask of MSM_SUBMIT_BO_x */ uint32_t handle;/* in, GEM handle */
Signature · 1 line
1.8.5.3
Re: [PATCH 7/7] drm/msm: validate flags, etc
Quoted 10 lines · Rob Clark
After reading a nice article on LWN[1], I went back and double checked my handling of invalid-input checking. Turns out there were a couple places I had missed.
Since the driver is fairly young, and the devices it supports are really only just barely usable for basic stuff (serial console) with an upstream kernel, I think we should fix this now and revert specific parts of this patch later in the unlikely event that a regression is reported.
Quoted 3 lines
[1] https://lwn.net/Articles/588444/
Signed-off-by Rob Clark <[email protected]>
Quoted 128 lines
--- drivers/gpu/drm/msm/msm_drv.c | 20 +++++++++++++++++++- drivers/gpu/drm/msm/msm_gem_submit.c | 15 +++++++++++++-- include/uapi/drm/msm_drm.h | 11 +++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-)drivers/gpu/drm/msm/msm_drv.c +19
-1@@ -664,6 +664,12 @@ static int msm_ioctl_gem_new(struct drm_device *dev, void *data, struct drm_file *file) {struct drm_msm_gem_new *args = data; + +if (args->flags & ~MSM_BO_FLAGS ) { +DRM_ERROR ("invalid flags: %08x\n" , args->flags); +return -EINVAL ; + } +return msm_gem_new_handle (dev, file, args->size, args->flags, &args->handle); }@@ -677,6 +683,11 @@ static int msm_ioctl_gem_cpu_prep(struct drm_device *dev, void *data,struct drm_gem_object *obj;int ret; +if (args->op & ~MSM_PREP_FLAGS ) { +DRM_ERROR ("invalid op: %08x\n" , args->op); +return -EINVAL ; + } + obj =drm_gem_object_lookup (dev, file, args->handle);if (!obj)return -ENOENT ;@@ -731,7 +742,14 @@ static int msm_ioctl_wait_fence(struct drm_device *dev, void *data, struct drm_file *file) {struct drm_msm_wait_fence *args = data; -return msm_wait_fence_interruptable (dev, args->fence, &TS (args->timeout)); + +if (args->pad) { +DRM_ERROR ("invalid pad: %08x\n" , args->pad); +return -EINVAL ; + } + +return msm_wait_fence_interruptable (dev, args->fence, + &TS (args->timeout)); }static const struct drm_ioctl_desc msm_ioctls[] = {drivers/gpu/drm/msm/msm_gem_submit.c +13
-2@@ -23,7 +23,6 @@* Cmdstream submission: */ -#define BO_INVALID_FLAGS ~(MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE)/* make sure these don't conflict w/ MSM_SUBMIT_BO_x */ #define BO_VALID 0x8000#define BO_LOCKED 0x4000@@ -77,7 +76,7 @@ static int submit_lookup_objects(struct msm_gem_submit *submit, goto out_unlock; } -if (submit_bo.flags &BO_INVALID_FLAGS ) { +if (submit_bo.flags & ~MSM_SUBMIT_BO_FLAGS ) {DRM_ERROR ("invalid flags: %x\n" , submit_bo.flags); ret = -EINVAL ; goto out_unlock;@@ -369,6 +368,18 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, goto out; } +/* validate input from userspace: */ +switch (submit_cmd.type) { +case MSM_SUBMIT_CMD_BUF : +case MSM_SUBMIT_CMD_IB_TARGET_BUF : +case MSM_SUBMIT_CMD_CTX_RESTORE_BUF : +break ; +default : +DRM_ERROR ("invalid type: %08x\n" , submit_cmd.type); + ret = -EINVAL ; + goto out; + } + ret =submit_bo (submit, submit_cmd.submit_idx, &msm_obj, &iova,NULL );if (ret)include/uapi/drm/msm_drm.h +11
-0@@ -70,6 +70,12 @@ struct drm_msm_param {#define MSM_BO_WC 0x00020000#define MSM_BO_UNCACHED 0x00040000 +#define MSM_BO_FLAGS (MSM_BO_SCANOUT | \ + MSM_BO_GPU_READONLY | \ + MSM_BO_CACHED | \ + MSM_BO_WC | \ + MSM_BO_UNCACHED) +struct drm_msm_gem_new {uint64_t size;/* in */ uint32_t flags;/* in, mask of MSM_BO_x */ @@ -86,6 +92,8 @@ struct drm_msm_gem_info {#define MSM_PREP_WRITE 0x02#define MSM_PREP_NOSYNC 0x04 +#define MSM_PREP_FLAGS (MSM_PREP_READ | MSM_PREP_WRITE | MSM_PREP_NOSYNC) +struct drm_msm_gem_cpu_prep {uint32_t handle;/* in */ uint32_t op;/* in, mask of MSM_PREP_x */ @@ -153,6 +161,9 @@ struct drm_msm_gem_submit_cmd {*/ #define MSM_SUBMIT_BO_READ 0x0001#define MSM_SUBMIT_BO_WRITE 0x0002 + +#define MSM_SUBMIT_BO_FLAGS (MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE) +struct drm_msm_gem_submit_bo {uint32_t flags;/* in, mask of MSM_SUBMIT_BO_x */ uint32_t handle;/* in, GEM handle */
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation