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
Signed-off-by: Rob Clark robdclark@gmail.com --- 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
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 4f977a5..5e1e6b0 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -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 \ diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index 6f1588a..6048b6b 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -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; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h index 41b29ad..9fafee6 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.h @@ -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: */
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_audio.c b/drivers/gpu/drm/msm/hdmi/hdmi_audio.c new file mode 100644 index 0000000..872485f --- /dev/null +++ b/drivers/gpu/drm/msm/hdmi/hdmi_audio.c @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark robdclark@gmail.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that 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 http://www.gnu.org/licenses/. + */ + +#include <linux/hdmi.h> +#include "hdmi.h" + + +/* Supported HDMI Audio channels */ +#define MSM_HDMI_AUDIO_CHANNEL_2 0 +#define MSM_HDMI_AUDIO_CHANNEL_4 1 +#define MSM_HDMI_AUDIO_CHANNEL_6 2 +#define MSM_HDMI_AUDIO_CHANNEL_8 3 + +/* maps MSM_HDMI_AUDIO_CHANNEL_n consts used by audio driver to # of channels: */ +static int nchannels[] = { 2, 4, 6, 8 }; + +/* Supported HDMI Audio sample rates */ +#define MSM_HDMI_SAMPLE_RATE_32KHZ 0 +#define MSM_HDMI_SAMPLE_RATE_44_1KHZ 1 +#define MSM_HDMI_SAMPLE_RATE_48KHZ 2 +#define MSM_HDMI_SAMPLE_RATE_88_2KHZ 3 +#define MSM_HDMI_SAMPLE_RATE_96KHZ 4 +#define MSM_HDMI_SAMPLE_RATE_176_4KHZ 5 +#define MSM_HDMI_SAMPLE_RATE_192KHZ 6 +#define MSM_HDMI_SAMPLE_RATE_MAX 7 + + +struct hdmi_msm_audio_acr { + uint32_t n; /* N parameter for clock regeneration */ + uint32_t cts; /* CTS parameter for clock regeneration */ +}; + +struct hdmi_msm_audio_arcs { + unsigned long int pixclock; + struct hdmi_msm_audio_acr lut[MSM_HDMI_SAMPLE_RATE_MAX]; +}; + +#define HDMI_MSM_AUDIO_ARCS(pclk, ...) { (1000 * (pclk)), __VA_ARGS__ } + +/* Audio constants lookup table for hdmi_msm_audio_acr_setup */ +/* Valid Pixel-Clock rates: 25.2MHz, 27MHz, 27.03MHz, 74.25MHz, 148.5MHz */ +static const struct hdmi_msm_audio_arcs acr_lut[] = { + /* 25.200MHz */ + HDMI_MSM_AUDIO_ARCS(25200, { + {4096, 25200}, {6272, 28000}, {6144, 25200}, {12544, 28000}, + {12288, 25200}, {25088, 28000}, {24576, 25200} }), + /* 27.000MHz */ + HDMI_MSM_AUDIO_ARCS(27000, { + {4096, 27000}, {6272, 30000}, {6144, 27000}, {12544, 30000}, + {12288, 27000}, {25088, 30000}, {24576, 27000} }), + /* 27.027MHz */ + HDMI_MSM_AUDIO_ARCS(27030, { + {4096, 27027}, {6272, 30030}, {6144, 27027}, {12544, 30030}, + {12288, 27027}, {25088, 30030}, {24576, 27027} }), + /* 74.250MHz */ + HDMI_MSM_AUDIO_ARCS(74250, { + {4096, 74250}, {6272, 82500}, {6144, 74250}, {12544, 82500}, + {12288, 74250}, {25088, 82500}, {24576, 74250} }), + /* 148.500MHz */ + HDMI_MSM_AUDIO_ARCS(148500, { + {4096, 148500}, {6272, 165000}, {6144, 148500}, {12544, 165000}, + {12288, 148500}, {25088, 165000}, {24576, 148500} }), +}; + +static const struct hdmi_msm_audio_arcs *get_arcs(unsigned long int pixclock) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(acr_lut); i++) { + const struct hdmi_msm_audio_arcs *arcs = &acr_lut[i]; + if (arcs->pixclock == pixclock) + return arcs; + } + + return NULL; +} + +int hdmi_audio_update(struct hdmi *hdmi) +{ + struct hdmi_audio *audio = &hdmi->audio; + struct hdmi_audio_infoframe *info = &audio->infoframe; + const struct hdmi_msm_audio_arcs *arcs = NULL; + bool enabled = audio->enabled; + uint32_t acr_pkt_ctrl, vbi_pkt_ctrl, aud_pkt_ctrl; + uint32_t infofrm_ctrl, audio_config; + + DBG("audio: enabled=%d, channels=%d, channel_allocation=0x%x, " + "level_shift_value=%d, downmix_inhibit=%d, rate=%d", + audio->enabled, info->channels, info->channel_allocation, + info->level_shift_value, info->downmix_inhibit, audio->rate); + DBG("video: power_on=%d, pixclock=%lu", hdmi->power_on, hdmi->pixclock); + + if (enabled && !(hdmi->power_on && hdmi->pixclock)) { + DBG("disabling audio: no video"); + enabled = false; + } + + if (enabled) { + arcs = get_arcs(hdmi->pixclock); + if (!arcs) { + DBG("disabling audio: unsupported pixclock: %lu", + hdmi->pixclock); + enabled = false; + } + } + + /* Read first before writing */ + acr_pkt_ctrl = hdmi_read(hdmi, REG_HDMI_ACR_PKT_CTRL); + vbi_pkt_ctrl = hdmi_read(hdmi, REG_HDMI_VBI_PKT_CTRL); + aud_pkt_ctrl = hdmi_read(hdmi, REG_HDMI_AUDIO_PKT_CTRL1); + infofrm_ctrl = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL0); + audio_config = hdmi_read(hdmi, REG_HDMI_AUDIO_CFG); + + /* Clear N/CTS selection bits */ + acr_pkt_ctrl &= ~HDMI_ACR_PKT_CTRL_SELECT__MASK; + + if (enabled) { + uint32_t n, cts, multiplier; + enum hdmi_acr_cts select; + uint8_t buf[14]; + + n = arcs->lut[audio->rate].n; + cts = arcs->lut[audio->rate].cts; + + if ((MSM_HDMI_SAMPLE_RATE_192KHZ == audio->rate) || + (MSM_HDMI_SAMPLE_RATE_176_4KHZ == audio->rate)) { + multiplier = 4; + n >>= 2; /* divide N by 4 and use multiplier */ + } else if ((MSM_HDMI_SAMPLE_RATE_96KHZ == audio->rate) || + (MSM_HDMI_SAMPLE_RATE_88_2KHZ == audio->rate)) { + multiplier = 2; + n >>= 1; /* divide N by 2 and use multiplier */ + } else { + multiplier = 1; + } + + DBG("n=%u, cts=%u, multiplier=%u", n, cts, multiplier); + + acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_SOURCE; + acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_AUDIO_PRIORITY; + acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_N_MULTIPLIER(multiplier); + + if ((MSM_HDMI_SAMPLE_RATE_48KHZ == audio->rate) || + (MSM_HDMI_SAMPLE_RATE_96KHZ == audio->rate) || + (MSM_HDMI_SAMPLE_RATE_192KHZ == audio->rate)) + select = ACR_48; + else if ((MSM_HDMI_SAMPLE_RATE_44_1KHZ == audio->rate) || + (MSM_HDMI_SAMPLE_RATE_88_2KHZ == audio->rate) || + (MSM_HDMI_SAMPLE_RATE_176_4KHZ == audio->rate)) + select = ACR_44; + else /* default to 32k */ + select = ACR_32; + + acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_SELECT(select); + + hdmi_write(hdmi, REG_HDMI_ACR_0(select - 1), + HDMI_ACR_0_CTS(cts)); + hdmi_write(hdmi, REG_HDMI_ACR_1(select - 1), + HDMI_ACR_1_N(n)); + + hdmi_write(hdmi, REG_HDMI_AUDIO_PKT_CTRL2, + COND(info->channels != 2, HDMI_AUDIO_PKT_CTRL2_LAYOUT) | + HDMI_AUDIO_PKT_CTRL2_OVERRIDE); + + acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_CONT; + acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_SEND; + + /* configure infoframe: */ + hdmi_audio_infoframe_pack(info, buf, sizeof(buf)); + hdmi_write(hdmi, REG_HDMI_AUDIO_INFO0, + (buf[3] << 0) || (buf[4] << 8) || + (buf[5] << 16) || (buf[6] << 24)); + hdmi_write(hdmi, REG_HDMI_AUDIO_INFO1, + (buf[7] << 0) || (buf[8] << 8)); + + hdmi_write(hdmi, REG_HDMI_GC, 0); + + vbi_pkt_ctrl |= HDMI_VBI_PKT_CTRL_GC_ENABLE; + vbi_pkt_ctrl |= HDMI_VBI_PKT_CTRL_GC_EVERY_FRAME; + + aud_pkt_ctrl |= HDMI_AUDIO_PKT_CTRL1_AUDIO_SAMPLE_SEND; + + infofrm_ctrl |= HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SEND; + infofrm_ctrl |= HDMI_INFOFRAME_CTRL0_AUDIO_INFO_CONT; + infofrm_ctrl |= HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE; + infofrm_ctrl |= HDMI_INFOFRAME_CTRL0_AUDIO_INFO_UPDATE; + + audio_config &= ~HDMI_AUDIO_CFG_FIFO_WATERMARK__MASK; + audio_config |= HDMI_AUDIO_CFG_FIFO_WATERMARK(4); + audio_config |= HDMI_AUDIO_CFG_ENGINE_ENABLE; + } else { + hdmi_write(hdmi, REG_HDMI_GC, HDMI_GC_MUTE); + acr_pkt_ctrl &= ~HDMI_ACR_PKT_CTRL_CONT; + acr_pkt_ctrl &= ~HDMI_ACR_PKT_CTRL_SEND; + vbi_pkt_ctrl &= ~HDMI_VBI_PKT_CTRL_GC_ENABLE; + vbi_pkt_ctrl &= ~HDMI_VBI_PKT_CTRL_GC_EVERY_FRAME; + aud_pkt_ctrl &= ~HDMI_AUDIO_PKT_CTRL1_AUDIO_SAMPLE_SEND; + infofrm_ctrl &= ~HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SEND; + infofrm_ctrl &= ~HDMI_INFOFRAME_CTRL0_AUDIO_INFO_CONT; + infofrm_ctrl &= ~HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE; + infofrm_ctrl &= ~HDMI_INFOFRAME_CTRL0_AUDIO_INFO_UPDATE; + audio_config &= ~HDMI_AUDIO_CFG_ENGINE_ENABLE; + } + + hdmi_write(hdmi, REG_HDMI_ACR_PKT_CTRL, acr_pkt_ctrl); + hdmi_write(hdmi, REG_HDMI_VBI_PKT_CTRL, vbi_pkt_ctrl); + hdmi_write(hdmi, REG_HDMI_AUDIO_PKT_CTRL1, aud_pkt_ctrl); + hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0, infofrm_ctrl); + + hdmi_write(hdmi, REG_HDMI_AUD_INT, + COND(enabled, HDMI_AUD_INT_AUD_FIFO_URUN_INT) | + COND(enabled, HDMI_AUD_INT_AUD_SAM_DROP_INT)); + + hdmi_write(hdmi, REG_HDMI_AUDIO_CFG, audio_config); + + + DBG("audio %sabled", enabled ? "en" : "dis"); + + return 0; +} + +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) +{ + struct hdmi_audio *audio; + + if (!hdmi) + return -ENXIO; + + audio = &hdmi->audio; + + if (num_of_channels >= ARRAY_SIZE(nchannels)) + return -EINVAL; + + audio->enabled = enabled; + audio->infoframe.channels = nchannels[num_of_channels]; + audio->infoframe.channel_allocation = channel_allocation; + audio->infoframe.level_shift_value = level_shift; + audio->infoframe.downmix_inhibit = down_mix; + + return hdmi_audio_update(hdmi); +} + +void hdmi_audio_set_sample_rate(struct hdmi *hdmi, int rate) +{ + struct hdmi_audio *audio; + + if (!hdmi) + return; + + audio = &hdmi->audio; + + if ((rate < 0) || (rate >= MSM_HDMI_SAMPLE_RATE_MAX)) + return; + + audio->rate = rate; + hdmi_audio_update(hdmi); +} diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index 7d10e55..f6cf745 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -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 = {
msm.hang_debug=y will dump out current register values if the gpu locks up, for easier debugging.
Signed-off-by: Rob Clark robdclark@gmail.com --- 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(-)
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c index 461df93..8b6fb84 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c @@ -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, diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index d321099..cf6eb97 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -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); diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h index ca11ea4..e16200d 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -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,
On 03/10/2014 10:47 AM, Rob Clark wrote:
msm.hang_debug=y will dump out current register values if the gpu locks up, for easier debugging.
Signed-off-by: Rob Clark robdclark@gmail.com
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(-)
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c index 461df93..8b6fb84 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c @@ -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);
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.
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,
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index d321099..cf6eb97 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -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));
+}
We should introduce you to the gooey wonderfulness of the snapshot :) All in due time.
Jordan
On Mon, Mar 10, 2014 at 4:17 PM, Jordan Crouse jcrouse@codeaurora.org wrote:
On 03/10/2014 10:47 AM, Rob Clark wrote:
msm.hang_debug=y will dump out current register values if the gpu locks up, for easier debugging.
Signed-off-by: Rob Clark robdclark@gmail.com
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(-)
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c index 461df93..8b6fb84 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c @@ -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);
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.
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.
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,
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index d321099..cf6eb97 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -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));
+}
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
Jordan
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Helper macro to simplify places where we need to poll with timeout waiting for gpu.
Signed-off-by: Rob Clark robdclark@gmail.com --- 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(-)
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c index 8b6fb84..59ed762 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c @@ -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? */ } diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index cf6eb97..7a11563 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -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[] = { diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h index e16200d..63c36ce 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -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) {
On 03/10/2014 10:47 AM, Rob Clark wrote:
Helper macro to simplify places where we need to poll with timeout waiting for gpu.
Signed-off-by: Rob Clark robdclark@gmail.com
Acked-by: Jordan Crouse jcrouse@codeaurora.org
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(-)
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c index 8b6fb84..59ed762 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c @@ -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? */ }
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index cf6eb97..7a11563 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -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[] = {
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h index e16200d..63c36ce 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -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) {
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.
Signed-off-by: Rob Clark robdclark@gmail.com --- 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(-)
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c index 59ed762..e6cb2bc 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c @@ -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
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index e6adafc..e913efa 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -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) diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 0cfe3f4..3e667ca 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -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);
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index 458db8c..fad2700 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -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));
On 03/10/2014 10:47 AM, Rob Clark wrote:
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.
Signed-off-by: Rob Clark robdclark@gmail.com
Acked-by: Jordan Crouse jcrouse@codeaurora.org
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(-)
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c index 59ed762..e6cb2bc 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c @@ -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
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index e6adafc..e913efa 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -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)
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 0cfe3f4..3e667ca 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -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);
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index 458db8c..fad2700 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -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));
On Mon, Mar 10, 2014 at 4:24 PM, Jordan Crouse jcrouse@codeaurora.org wrote:
On 03/10/2014 10:47 AM, Rob Clark wrote:
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
Signed-off-by: Rob Clark robdclark@gmail.com
Acked-by: Jordan Crouse jcrouse@codeaurora.org
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(-)
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c index 59ed762..e6cb2bc 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c @@ -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);
} #endifmutex_unlock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index e6adafc..e913efa 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -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)
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 0cfe3f4..3e667ca 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -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);
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index 458db8c..fad2700 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -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;
#define DRM_MSM_HANGCHECK_PERIOD 500 /* in ms */ #define DRM_MSM_HANGCHECK_JIFFIESstruct work_struct inactive_work;
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));
-- The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
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.
Signed-off-by: Rob Clark robdclark@gmail.com --- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 6 ++++++ include/uapi/drm/msm_drm.h | 1 + 2 files changed, 7 insertions(+)
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 7a11563..28ca8cd 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -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; diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h index d3c6207..bf91a78 100644 --- a/include/uapi/drm/msm_drm.h +++ b/include/uapi/drm/msm_drm.h @@ -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 0x03
struct drm_msm_param { uint32_t pipe; /* in, MSM_PIPE_x */
On 03/10/2014 10:47 AM, Rob Clark wrote:
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.
Signed-off-by: Rob Clark robdclark@gmail.com
Acked-by: Jordan Crouse jcrouse@codeaurora.org
drivers/gpu/drm/msm/adreno/adreno_gpu.c | 6 ++++++ include/uapi/drm/msm_drm.h | 1 + 2 files changed, 7 insertions(+)
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 7a11563..28ca8cd 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -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);
default: DBG("%s: invalid param: %u", gpu->name, param); return -EINVAL;return 0;
diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h index d3c6207..bf91a78 100644 --- a/include/uapi/drm/msm_drm.h +++ b/include/uapi/drm/msm_drm.h @@ -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 0x03
struct drm_msm_param { uint32_t pipe; /* in, MSM_PIPE_x */
Signed-off-by: Rob Clark robdclark@gmail.com --- 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(-)
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c index e6cb2bc..913cf8a 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c @@ -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; }
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index 6048b6b..d855bcf 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -17,8 +17,6 @@
#include "hdmi.h"
-static struct platform_device *hdmi_pdev; - void hdmi_set_mode(struct hdmi *hdmi, bool power_on) { uint32_t ctrl = 0; @@ -68,8 +66,6 @@ void hdmi_destroy(struct kref *kref) hdmi_i2c_destroy(hdmi->i2c);
platform_set_drvdata(hdmi->pdev, NULL); - - put_device(&hdmi->pdev->dev); }
/* initialize connector */ @@ -77,7 +73,7 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder) { struct hdmi *hdmi = NULL; struct msm_drm_private *priv = dev->dev_private; - struct platform_device *pdev = hdmi_pdev; + struct platform_device *pdev = priv->hdmi_pdev; struct hdmi_platform_config *config; int i, ret;
@@ -97,8 +93,6 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
kref_init(&hdmi->refcount);
- get_device(&pdev->dev); - hdmi->dev = dev; hdmi->pdev = pdev; hdmi->config = config; @@ -255,17 +249,24 @@ fail:
#include <linux/of_gpio.h>
-static int hdmi_dev_probe(struct platform_device *pdev) +static void set_hdmi_pdev(struct drm_device *dev, + struct platform_device *pdev) +{ + struct msm_drm_private *priv = dev->dev_private; + priv->hdmi_pdev = pdev; +} + +static int hdmi_bind(struct device *dev, struct device *master, void *data) { static struct hdmi_platform_config config = {}; #ifdef CONFIG_OF - struct device_node *of_node = pdev->dev.of_node; + struct device_node *of_node = dev->of_node;
int get_gpio(const char *name) { int gpio = of_get_named_gpio(of_node, name, 0); if (gpio < 0) { - dev_err(&pdev->dev, "failed to get gpio: %s (%d)\n", + dev_err(dev, "failed to get gpio: %s (%d)\n", name, gpio); gpio = -1; } @@ -342,14 +343,30 @@ static int hdmi_dev_probe(struct platform_device *pdev) config.mux_sel_gpio = -1; } #endif - pdev->dev.platform_data = &config; - hdmi_pdev = pdev; + dev->platform_data = &config; + set_hdmi_pdev(dev_get_drvdata(master), to_platform_device(dev)); return 0; }
+static void hdmi_unbind(struct device *dev, struct device *master, + void *data) +{ + set_hdmi_pdev(dev_get_drvdata(master), NULL); +} + +static const struct component_ops hdmi_ops = { + .bind = hdmi_bind, + .unbind = hdmi_unbind, +}; + +static int hdmi_dev_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &hdmi_ops); +} + static int hdmi_dev_remove(struct platform_device *pdev) { - hdmi_pdev = NULL; + component_del(&pdev->dev, &hdmi_ops); return 0; }
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index e913efa..9ffc275 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -56,6 +56,10 @@ static char *vram; MODULE_PARM_DESC(vram, "Configure VRAM size (for devices without IOMMU/GPUMMU"); module_param(vram, charp, 0);
+/* + * Util/helpers: + */ + void __iomem *msm_ioremap(struct platform_device *pdev, const char *name, const char *dbgname) { @@ -143,6 +147,8 @@ static int msm_unload(struct drm_device *dev) priv->vram.paddr, &attrs); }
+ component_unbind_all(dev->dev, dev); + dev->dev_private = NULL;
kfree(priv); @@ -175,6 +181,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags) struct msm_kms *kms; int ret;
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { dev_err(dev->dev, "failed to allocate private data\n"); @@ -226,6 +233,13 @@ static int msm_load(struct drm_device *dev, unsigned long flags) (uint32_t)(priv->vram.paddr + size)); }
+ platform_set_drvdata(pdev, dev); + + /* Bind all our sub-components: */ + ret = component_bind_all(dev->dev, dev); + if (ret) + return ret; + switch (get_mdp_ver(pdev)) { case 4: kms = mdp4_kms_init(dev); @@ -281,8 +295,6 @@ static int msm_load(struct drm_device *dev, unsigned long flags) goto fail; }
- platform_set_drvdata(pdev, dev); - #ifdef CONFIG_DRM_MSM_FBDEV priv->fbdev = msm_fbdev_init(dev); #endif @@ -824,18 +836,131 @@ static const struct dev_pm_ops msm_pm_ops = { };
/* + * Componentized driver support: + */ + +#ifdef CONFIG_OF +/* NOTE: the CONFIG_OF case duplicates the same code as exynos or imx + * (or probably any other).. so probably some room for some helpers + */ +static int compare_parent_of(struct device *dev, void *data) +{ + struct of_phandle_args *args = data; + return dev->parent && dev->parent->of_node == args->np; +} + +static int compare_of(struct device *dev, void *data) +{ + return dev->of_node == data; +} + +static int msm_drm_add_components(struct device *master, struct master *m) +{ + struct device_node *np = master->of_node; + unsigned i; + int ret; + + for (i = 0; ; i++) { + struct of_phandle_args args; + + ret = of_parse_phandle_with_fixed_args(np, "crtcs", 1, + i, &args); + if (ret) + break; + + ret = component_master_add_child(m, compare_parent_of, &args); + of_node_put(args.np); + + if (ret) + return ret; + } + + for (i = 0; ; i++) { + struct device_node *node; + + node = of_parse_phandle(np, "connectors", i); + if (!node) + break; + + ret = component_master_add_child(m, compare_of, node); + of_node_put(node); + + if (ret) + return ret; + } + return 0; +} +#else +static int compare_dev(struct device *dev, void *data) +{ + return dev == data; +} + +static int msm_drm_add_components(struct device *master, struct master *m) +{ + /* For non-DT case, it kinda sucks. We don't actually have a way + * to know whether or not we are waiting for certain devices (or if + * they are simply not present). But for non-DT we only need to + * care about apq8064/apq8060/etc (all mdp4/a3xx): + */ + static const char *devnames[] = { + "hdmi_msm.0", "kgsl-3d0.0", + }; + int i; + + DBG("Adding components.."); + + for (i = 0; i < ARRAY_SIZE(devnames); i++) { + struct device *dev; + int ret; + + dev = bus_find_device_by_name(&platform_bus_type, + NULL, devnames[i]); + if (!dev) { + dev_info(master, "still waiting for %s\n", devnames[i]); + return -EPROBE_DEFER; + } + + ret = component_master_add_child(m, compare_dev, dev); + if (ret) { + DBG("could not add child: %d", ret); + return ret; + } + } + + return 0; +} +#endif + +static int msm_drm_bind(struct device *dev) +{ + return drm_platform_init(&msm_driver, to_platform_device(dev)); +} + +static void msm_drm_unbind(struct device *dev) +{ + drm_put_dev(platform_get_drvdata(to_platform_device(dev))); +} + +static const struct component_master_ops msm_drm_ops = { + .add_components = msm_drm_add_components, + .bind = msm_drm_bind, + .unbind = msm_drm_unbind, +}; + +/* * Platform driver: */
static int msm_pdev_probe(struct platform_device *pdev) { pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); - return drm_platform_init(&msm_driver, pdev); + return component_master_add(&pdev->dev, &msm_drm_ops); }
static int msm_pdev_remove(struct platform_device *pdev) { - drm_put_dev(platform_get_drvdata(pdev)); + component_master_del(&pdev->dev, &msm_drm_ops);
return 0; } diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 3d63269..9d10ee0 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -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;
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.
[1] https://lwn.net/Articles/588444/
Signed-off-by: Rob Clark robdclark@gmail.com --- 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(-)
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 9ffc275..eee8d37 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -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[] = { diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 5423e91..1f1f4cf 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -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) diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h index bf91a78..0664c31 100644 --- a/include/uapi/drm/msm_drm.h +++ b/include/uapi/drm/msm_drm.h @@ -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 */
On 03/10/2014 10:47 AM, Rob Clark wrote:
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.
[1] https://lwn.net/Articles/588444/
Signed-off-by: Rob Clark robdclark@gmail.com
Acked-by: Jordan Crouse jcrouse@codeaurora.org
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(-)
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 9ffc275..eee8d37 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -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[] = {
diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 5423e91..1f1f4cf 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -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)
diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h index bf91a78..0664c31 100644 --- a/include/uapi/drm/msm_drm.h +++ b/include/uapi/drm/msm_drm.h @@ -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 */
dri-devel@lists.freedesktop.org