From 8a9d4626db312fe0a682c35ef43fe8d9160c2ecd Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Fri, 28 Aug 2015 17:21:46 +0300 Subject: [PATCH 01/12] OMAPDSS: hdmi: Reconfigure and restart audio when display is enabled Reconfigure and restart audio when display is enabled, if audio playback was active before. This is needed in a situation when an audio+video stream application opens the audio stream before the video. When video stream is opened the display mode may change and that aborts audio playback, because the display is momentarily turned off. The audio configuration is stored when it is successfully applied and a boolean is set when the audio playback is started and unset when stopped. This data is used to reconfigure the audio when display is re-enabled. The audio playback is aborted if the reconfiguration fails. A new spin lock is introduced in order to protect state variables related to audio playback status. This is needed for the transition from display enabled state (when audio start/stop commands can be written to HW) to display disabled state (when audio start/stop commands update only the hdmi.audio_playing variable) to always serialize correctly with the start/stop audio commands. The already existing mutex can not be used, because the audio start and stop commands are executed in atomic context. For example: when display is turned back on we take the spinlock and we can be sure that the audio start/stop status will not change while we update the HW according to hdmi.audio_playing state and set hdmi.display_enabled to true. After releasing the lock hdmi.display_enabled is true and all audio_start and audio_stop commands write their stuff directly to HW. Signed-off-by: Jyri Sarha Signed-off-by: Tomi Valkeinen --- drivers/video/fbdev/omap2/dss/hdmi.h | 9 +++- drivers/video/fbdev/omap2/dss/hdmi4.c | 66 +++++++++++++++++++---- drivers/video/fbdev/omap2/dss/hdmi5.c | 75 +++++++++++++++++++++------ 3 files changed, 123 insertions(+), 27 deletions(-) diff --git a/drivers/video/fbdev/omap2/dss/hdmi.h b/drivers/video/fbdev/omap2/dss/hdmi.h index e4a32fe77b02..53616b02b613 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi.h +++ b/drivers/video/fbdev/omap2/dss/hdmi.h @@ -351,13 +351,20 @@ struct omap_hdmi { struct regulator *vdda_reg; bool core_enabled; - bool display_enabled; struct omap_dss_device output; struct platform_device *audio_pdev; void (*audio_abort_cb)(struct device *dev); int wp_idlemode; + + bool audio_configured; + struct omap_dss_audio audio_config; + + /* This lock should be taken when booleans bellow are touched. */ + spinlock_t audio_playing_lock; + bool audio_playing; + bool display_enabled; }; #endif diff --git a/drivers/video/fbdev/omap2/dss/hdmi4.c b/drivers/video/fbdev/omap2/dss/hdmi4.c index 6d3aa3f51c20..94c8d5549b4c 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi4.c +++ b/drivers/video/fbdev/omap2/dss/hdmi4.c @@ -321,9 +321,22 @@ static int read_edid(u8 *buf, int len) return r; } +static void hdmi_start_audio_stream(struct omap_hdmi *hd) +{ + hdmi_wp_audio_enable(&hd->wp, true); + hdmi4_audio_start(&hd->core, &hd->wp); +} + +static void hdmi_stop_audio_stream(struct omap_hdmi *hd) +{ + hdmi4_audio_stop(&hd->core, &hd->wp); + hdmi_wp_audio_enable(&hd->wp, false); +} + static int hdmi_display_enable(struct omap_dss_device *dssdev) { struct omap_dss_device *out = &hdmi.output; + unsigned long flags; int r = 0; DSSDBG("ENTER hdmi_display_enable\n"); @@ -342,7 +355,21 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev) goto err0; } + if (hdmi.audio_configured) { + r = hdmi4_audio_config(&hdmi.core, &hdmi.wp, &hdmi.audio_config, + hdmi.cfg.timings.pixelclock); + if (r) { + DSSERR("Error restoring audio configuration: %d", r); + hdmi.audio_abort_cb(&hdmi.pdev->dev); + hdmi.audio_configured = false; + } + } + + spin_lock_irqsave(&hdmi.audio_playing_lock, flags); + if (hdmi.audio_configured && hdmi.audio_playing) + hdmi_start_audio_stream(&hdmi); hdmi.display_enabled = true; + spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags); mutex_unlock(&hdmi.lock); return 0; @@ -354,17 +381,19 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev) static void hdmi_display_disable(struct omap_dss_device *dssdev) { + unsigned long flags; + DSSDBG("Enter hdmi_display_disable\n"); mutex_lock(&hdmi.lock); - if (hdmi.audio_pdev && hdmi.audio_abort_cb) - hdmi.audio_abort_cb(&hdmi.audio_pdev->dev); + spin_lock_irqsave(&hdmi.audio_playing_lock, flags); + hdmi_stop_audio_stream(&hdmi); + hdmi.display_enabled = false; + spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags); hdmi_power_off_full(dssdev); - hdmi.display_enabled = false; - mutex_unlock(&hdmi.lock); } @@ -568,6 +597,8 @@ static int hdmi_audio_shutdown(struct device *dev) mutex_lock(&hd->lock); hd->audio_abort_cb = NULL; + hd->audio_configured = false; + hd->audio_playing = false; mutex_unlock(&hd->lock); return 0; @@ -576,25 +607,34 @@ static int hdmi_audio_shutdown(struct device *dev) static int hdmi_audio_start(struct device *dev) { struct omap_hdmi *hd = dev_get_drvdata(dev); + unsigned long flags; WARN_ON(!hdmi_mode_has_audio(&hd->cfg)); - WARN_ON(!hd->display_enabled); - hdmi_wp_audio_enable(&hd->wp, true); - hdmi4_audio_start(&hd->core, &hd->wp); + spin_lock_irqsave(&hd->audio_playing_lock, flags); + if (hd->display_enabled) + hdmi_start_audio_stream(hd); + hd->audio_playing = true; + + spin_unlock_irqrestore(&hd->audio_playing_lock, flags); return 0; } static void hdmi_audio_stop(struct device *dev) { struct omap_hdmi *hd = dev_get_drvdata(dev); + unsigned long flags; WARN_ON(!hdmi_mode_has_audio(&hd->cfg)); - WARN_ON(!hd->display_enabled); - hdmi4_audio_stop(&hd->core, &hd->wp); - hdmi_wp_audio_enable(&hd->wp, false); + spin_lock_irqsave(&hd->audio_playing_lock, flags); + + if (hd->display_enabled) + hdmi_stop_audio_stream(hd); + hd->audio_playing = false; + + spin_unlock_irqrestore(&hd->audio_playing_lock, flags); } static int hdmi_audio_config(struct device *dev, @@ -612,7 +652,10 @@ static int hdmi_audio_config(struct device *dev, ret = hdmi4_audio_config(&hd->core, &hd->wp, dss_audio, hd->cfg.timings.pixelclock); - + if (!ret) { + hd->audio_configured = true; + hd->audio_config = *dss_audio; + } out: mutex_unlock(&hd->lock); @@ -657,6 +700,7 @@ static int hdmi4_bind(struct device *dev, struct device *master, void *data) dev_set_drvdata(&pdev->dev, &hdmi); mutex_init(&hdmi.lock); + spin_lock_init(&hdmi.audio_playing_lock); if (pdev->dev.of_node) { r = hdmi_probe_of(pdev); diff --git a/drivers/video/fbdev/omap2/dss/hdmi5.c b/drivers/video/fbdev/omap2/dss/hdmi5.c index 7f875788edbc..b59ba7902be1 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi5.c +++ b/drivers/video/fbdev/omap2/dss/hdmi5.c @@ -349,9 +349,24 @@ static int read_edid(u8 *buf, int len) return r; } +static void hdmi_start_audio_stream(struct omap_hdmi *hd) +{ + REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2); + hdmi_wp_audio_enable(&hd->wp, true); + hdmi_wp_audio_core_req_enable(&hd->wp, true); +} + +static void hdmi_stop_audio_stream(struct omap_hdmi *hd) +{ + hdmi_wp_audio_core_req_enable(&hd->wp, false); + hdmi_wp_audio_enable(&hd->wp, false); + REG_FLD_MOD(hd->wp.base, HDMI_WP_SYSCONFIG, hd->wp_idlemode, 3, 2); +} + static int hdmi_display_enable(struct omap_dss_device *dssdev) { struct omap_dss_device *out = &hdmi.output; + unsigned long flags; int r = 0; DSSDBG("ENTER hdmi_display_enable\n"); @@ -370,7 +385,21 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev) goto err0; } + if (hdmi.audio_configured) { + r = hdmi5_audio_config(&hdmi.core, &hdmi.wp, &hdmi.audio_config, + hdmi.cfg.timings.pixelclock); + if (r) { + DSSERR("Error restoring audio configuration: %d", r); + hdmi.audio_abort_cb(&hdmi.pdev->dev); + hdmi.audio_configured = false; + } + } + + spin_lock_irqsave(&hdmi.audio_playing_lock, flags); + if (hdmi.audio_configured && hdmi.audio_playing) + hdmi_start_audio_stream(&hdmi); hdmi.display_enabled = true; + spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags); mutex_unlock(&hdmi.lock); return 0; @@ -382,17 +411,19 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev) static void hdmi_display_disable(struct omap_dss_device *dssdev) { + unsigned long flags; + DSSDBG("Enter hdmi_display_disable\n"); mutex_lock(&hdmi.lock); - if (hdmi.audio_pdev && hdmi.audio_abort_cb) - hdmi.audio_abort_cb(&hdmi.audio_pdev->dev); + spin_lock_irqsave(&hdmi.audio_playing_lock, flags); + hdmi_stop_audio_stream(&hdmi); + hdmi.display_enabled = false; + spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags); hdmi_power_off_full(dssdev); - hdmi.display_enabled = false; - mutex_unlock(&hdmi.lock); } @@ -596,6 +627,8 @@ static int hdmi_audio_shutdown(struct device *dev) mutex_lock(&hd->lock); hd->audio_abort_cb = NULL; + hd->audio_configured = false; + hd->audio_playing = false; mutex_unlock(&hd->lock); return 0; @@ -604,32 +637,34 @@ static int hdmi_audio_shutdown(struct device *dev) static int hdmi_audio_start(struct device *dev) { struct omap_hdmi *hd = dev_get_drvdata(dev); + unsigned long flags; WARN_ON(!hdmi_mode_has_audio(&hd->cfg)); - WARN_ON(!hd->display_enabled); - /* No-idle while playing audio, store the old value */ - hd->wp_idlemode = REG_GET(hdmi.wp.base, HDMI_WP_SYSCONFIG, 3, 2); - REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2); + spin_lock_irqsave(&hd->audio_playing_lock, flags); - hdmi_wp_audio_enable(&hd->wp, true); - hdmi_wp_audio_core_req_enable(&hd->wp, true); + if (hd->display_enabled) + hdmi_start_audio_stream(hd); + hd->audio_playing = true; + spin_unlock_irqrestore(&hd->audio_playing_lock, flags); return 0; } static void hdmi_audio_stop(struct device *dev) { struct omap_hdmi *hd = dev_get_drvdata(dev); + unsigned long flags; WARN_ON(!hdmi_mode_has_audio(&hd->cfg)); - WARN_ON(!hd->display_enabled); - hdmi_wp_audio_core_req_enable(&hd->wp, false); - hdmi_wp_audio_enable(&hd->wp, false); + spin_lock_irqsave(&hd->audio_playing_lock, flags); - /* Playback stopped, restore original idlemode */ - REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, hd->wp_idlemode, 3, 2); + if (hd->display_enabled) + hdmi_stop_audio_stream(hd); + hd->audio_playing = false; + + spin_unlock_irqrestore(&hd->audio_playing_lock, flags); } static int hdmi_audio_config(struct device *dev, @@ -648,6 +683,10 @@ static int hdmi_audio_config(struct device *dev, ret = hdmi5_audio_config(&hd->core, &hd->wp, dss_audio, hd->cfg.timings.pixelclock); + if (!ret) { + hd->audio_configured = true; + hd->audio_config = *dss_audio; + } out: mutex_unlock(&hd->lock); @@ -678,6 +717,11 @@ static int hdmi_audio_register(struct device *dev) if (IS_ERR(hdmi.audio_pdev)) return PTR_ERR(hdmi.audio_pdev); + hdmi_runtime_get(); + hdmi.wp_idlemode = + REG_GET(hdmi.wp.base, HDMI_WP_SYSCONFIG, 3, 2); + hdmi_runtime_put(); + return 0; } @@ -692,6 +736,7 @@ static int hdmi5_bind(struct device *dev, struct device *master, void *data) dev_set_drvdata(&pdev->dev, &hdmi); mutex_init(&hdmi.lock); + spin_lock_init(&hdmi.audio_playing_lock); if (pdev->dev.of_node) { r = hdmi_probe_of(pdev); From c50d5cd235210abffa1e88affbead1adbd5f294a Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Mon, 21 Sep 2015 15:34:00 +0200 Subject: [PATCH 02/12] video/omap: remove invalid check regno is unsigned so it cannot be negative. The problem has been detected using proposed semantic patch scripts/coccinelle/tests/unsigned_lesser_than_zero.cocci [1]. [1]: http://permalink.gmane.org/gmane.linux.kernel/2038576 Signed-off-by: Andrzej Hajda Signed-off-by: Tomi Valkeinen --- drivers/video/fbdev/omap/omapfb_main.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/video/fbdev/omap/omapfb_main.c b/drivers/video/fbdev/omap/omapfb_main.c index 1fb3ea3c98a1..393ae1bc07e8 100644 --- a/drivers/video/fbdev/omap/omapfb_main.c +++ b/drivers/video/fbdev/omap/omapfb_main.c @@ -276,11 +276,6 @@ static int _setcolreg(struct fb_info *info, u_int regno, u_int red, u_int green, if (r != 0) break; - if (regno < 0) { - r = -EINVAL; - break; - } - if (regno < 16) { u16 pal; pal = ((red >> (16 - var->red.length)) << From 48846ec502525bd93ee6e611eeacb397f5662f9b Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 8 Sep 2015 21:19:50 +0200 Subject: [PATCH 03/12] fbdev: ssd1307fb: alphabetize headers This patch sorts the headers on ssd1307fb driver. Signed-off-by: Olliver Schinagl Signed-off-by: Tomi Valkeinen --- drivers/video/fbdev/ssd1307fb.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index 93f4c902d0f9..8c01108f0a80 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -6,16 +6,16 @@ * Licensed under the GPLv2 or later. */ -#include #include -#include -#include +#include #include -#include +#include +#include +#include #include #include #include -#include +#include #define SSD1307FB_DATA 0x40 #define SSD1307FB_COMMAND 0x80 From 3ac58d58087e65592f2b91957c7a106a7a409aa4 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 8 Sep 2015 21:19:51 +0200 Subject: [PATCH 04/12] fbdev: ssd1307fb: add ssd1309 support The ssd1307fb driver supports a lot of chips from the ssd130xfb series. This patch adds the ssd1309 chip, a 128x64 OLED driver chip. It is very similar to the other chips and only has some definitions added to support it. Signed-off-by: Olliver Schinagl Acked-by: Rob Herring Acked-by: Prabhakar Lad Signed-off-by: Tomi Valkeinen --- Documentation/devicetree/bindings/video/ssd1307fb.txt | 3 ++- drivers/video/fbdev/ssd1307fb.c | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/video/ssd1307fb.txt b/Documentation/devicetree/bindings/video/ssd1307fb.txt index d1be78db63f5..eb31ed47a283 100644 --- a/Documentation/devicetree/bindings/video/ssd1307fb.txt +++ b/Documentation/devicetree/bindings/video/ssd1307fb.txt @@ -2,7 +2,8 @@ Required properties: - compatible: Should be "solomon,fb-". The only supported bus for - now is i2c, and the supported chips are ssd1305, ssd1306 and ssd1307. + now is i2c, and the supported chips are ssd1305, ssd1306, ssd1307 and + ssd1309. - reg: Should contain address of the controller on the I2C bus. Most likely 0x3c or 0x3d - pwm: Should contain the pwm to use according to the OF device tree PWM diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index 8c01108f0a80..fa3480815cdb 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -495,6 +495,12 @@ static struct ssd1307fb_deviceinfo ssd1307fb_ssd1307_deviceinfo = { .need_pwm = 1, }; +static struct ssd1307fb_deviceinfo ssd1307fb_ssd1309_deviceinfo = { + .default_vcomh = 0x34, + .default_dclk_div = 1, + .default_dclk_frq = 10, +}; + static const struct of_device_id ssd1307fb_of_match[] = { { .compatible = "solomon,ssd1305fb-i2c", @@ -508,6 +514,10 @@ static const struct of_device_id ssd1307fb_of_match[] = { .compatible = "solomon,ssd1307fb-i2c", .data = (void *)&ssd1307fb_ssd1307_deviceinfo, }, + { + .compatible = "solomon,ssd1309fb-i2c", + .data = (void *)&ssd1307fb_ssd1309_deviceinfo, + }, {}, }; MODULE_DEVICE_TABLE(of, ssd1307fb_of_match); @@ -709,6 +719,7 @@ static const struct i2c_device_id ssd1307fb_i2c_id[] = { { "ssd1305fb", 0 }, { "ssd1306fb", 0 }, { "ssd1307fb", 0 }, + { "ssd1309fb", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id); From ec90631c8a6861cdd752148fe19c20f775df47e7 Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Fri, 25 Sep 2015 00:14:13 +0200 Subject: [PATCH 05/12] fb_ddc: Allow I2C adapters without SCL read capability i2c-algo-bit allows I2C adapters without SCL read capability to work but fb_ddc_read fails to work on them. Fix fb_ddc_read to work with I2C adapters not capable of reading SCL. Signed-off-by: Ondrej Zary Signed-off-by: Tomi Valkeinen --- drivers/video/fbdev/core/fb_ddc.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/drivers/video/fbdev/core/fb_ddc.c b/drivers/video/fbdev/core/fb_ddc.c index 94322ccfedde..8bf5f2f54be7 100644 --- a/drivers/video/fbdev/core/fb_ddc.c +++ b/drivers/video/fbdev/core/fb_ddc.c @@ -67,13 +67,17 @@ unsigned char *fb_ddc_read(struct i2c_adapter *adapter) msleep(13); algo_data->setscl(algo_data->data, 1); - for (j = 0; j < 5; j++) { - msleep(10); - if (algo_data->getscl(algo_data->data)) - break; + if (algo_data->getscl) { + for (j = 0; j < 5; j++) { + msleep(10); + if (algo_data->getscl(algo_data->data)) + break; + } + if (j == 5) + continue; + } else { + udelay(algo_data->udelay); } - if (j == 5) - continue; algo_data->setsda(algo_data->data, 0); msleep(15); @@ -89,10 +93,14 @@ unsigned char *fb_ddc_read(struct i2c_adapter *adapter) msleep(15); algo_data->setscl(algo_data->data, 1); - for (j = 0; j < 10; j++) { - msleep(10); - if (algo_data->getscl(algo_data->data)) - break; + if (algo_data->getscl) { + for (j = 0; j < 10; j++) { + msleep(10); + if (algo_data->getscl(algo_data->data)) + break; + } + } else { + udelay(algo_data->udelay); } algo_data->setsda(algo_data->data, 1); From 6a5e3bd0c8bc1025bb5092f54d5aea38216c665e Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Fri, 25 Sep 2015 00:14:14 +0200 Subject: [PATCH 06/12] tridentfb: Add DDC support Add DDC support for Trident cards. Tested on TGUI9440, TGUI9680, 3DImage 9750, Blade3D 9880 and Blade XP. Signed-off-by: Ondrej Zary Signed-off-by: Tomi Valkeinen --- drivers/video/fbdev/Kconfig | 2 + drivers/video/fbdev/tridentfb.c | 182 +++++++++++++++++++++++++++++++- 2 files changed, 179 insertions(+), 5 deletions(-) diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 8b1d371b5404..5d67226a1cee 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -1666,6 +1666,8 @@ config FB_TRIDENT select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT + select FB_DDC + select FB_MODE_HELPERS ---help--- This is the frame buffer device driver for Trident PCI/AGP chipsets. Supported chipset families are TGUI 9440/96XX, 3DImage, Blade3D diff --git a/drivers/video/fbdev/tridentfb.c b/drivers/video/fbdev/tridentfb.c index 7ed9a227f5ea..e5c9b145460e 100644 --- a/drivers/video/fbdev/tridentfb.c +++ b/drivers/video/fbdev/tridentfb.c @@ -25,6 +25,9 @@ #include