Commit 15b58830 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'fbdev-omap-3.16' of git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux into next

Pull omap fbdev changes from Tomi Valkeinen:
 - DT support for the panel drivers that were still missing it
 - TI AM43xx support
 - TI OMAP5 support

* tag 'fbdev-omap-3.16' of git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux: (46 commits)
  OMAPDSS: move 'compatible' converter to omapdss driver
  OMAPDSS: HDMI: fix devm_ioremap_resource error checks
  OMAPDSS: HDMI: remove unused defines
  OMAPDSS: HDMI: cleanup WP ioremaps
  OMAPDSS: panel NEC-NL8048HL11 DT support
  Doc/DT: Add DT binding documentation for TPO td043mtea1 panel
  OMAPDSS: Panel TPO-TD043MTEA1 DT support
  Doc/DT: Add DT binding documentation for SHARP LS037V7DW01
  OMAPDSS: panel sharp-ls037v7dw01 DT support
  OMAPDSS: panel-sharp-ls037v7dw01: update to use gpiod
  Doc/DT: Add binding doc for lgphilips,lb035q02.txt
  OMAPDSS: panel-lgphilips-lb035q02: Add DT support
  OMAPDSS: panel-lgphilips-lb035q02: use gpiod for enable gpio
  OMAPDSS: hdmi5_core: Fix compilation with OMAP5_DSS_HDMI_AUDIO
  OMAPDSS: panel-dpi: enable-gpio
  OMAPDSS: Fix writes to DISPC_POL_FREQ
  Doc/DT: Add OMAP5 DSS DT bindings
  OMAPDSS: HDMI: cleanup ioremaps
  OMAPDSS: HDMI: Add OMAP5 HDMI support
  OMAPDSS: HDMI: PLL changes for OMAP5
  ...
parents d55696af f2dd36ac
......@@ -7,6 +7,7 @@ Required properties:
Optional properties:
- label: a symbolic name for the connector
- hpd-gpios: HPD GPIO number
Required nodes:
- Video port for HDMI input
......
LG.Philips LB035Q02 Panel
=========================
Required properties:
- compatible: "lgphilips,lb035q02"
- enable-gpios: panel enable gpio
Optional properties:
- label: a symbolic name for the panel
Required nodes:
- Video port for DPI input
Example
-------
lcd-panel: panel@0 {
compatible = "lgphilips,lb035q02";
reg = <0>;
spi-max-frequency = <100000>;
spi-cpol;
spi-cpha;
label = "lcd";
enable-gpios = <&gpio7 7 0>;
port {
lcd_in: endpoint {
remote-endpoint = <&dpi_out>;
};
};
};
Generic MIPI DPI Panel
======================
Required properties:
- compatible: "panel-dpi"
Optional properties:
- label: a symbolic name for the panel
- enable-gpios: panel enable gpio
Required nodes:
- "panel-timing" containing video timings
(Documentation/devicetree/bindings/video/display-timing.txt)
- Video port for DPI input
Example
-------
lcd0: display@0 {
compatible = "samsung,lte430wq-f0c", "panel-dpi";
label = "lcd";
port {
lcd_in: endpoint {
remote-endpoint = <&dpi_out>;
};
};
panel-timing {
clock-frequency = <9200000>;
hactive = <480>;
vactive = <272>;
hfront-porch = <8>;
hback-porch = <4>;
hsync-len = <41>;
vback-porch = <2>;
vfront-porch = <4>;
vsync-len = <10>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <1>;
pixelclk-active = <1>;
};
};
SHARP LS037V7DW01 TFT-LCD panel
===================================
Required properties:
- compatible: "sharp,ls037v7dw01"
Optional properties:
- label: a symbolic name for the panel
- enable-gpios: a GPIO spec for the optional enable pin.
This pin is the INI pin as specified in the LS037V7DW01.pdf file.
- reset-gpios: a GPIO spec for the optional reset pin.
This pin is the RESB pin as specified in the LS037V7DW01.pdf file.
- mode-gpios: a GPIO
ordered MO, LR, and UD as specified in the LS037V7DW01.pdf file.
Required nodes:
- Video port for DPI input
This panel can have zero to five GPIOs to configure to change configuration
between QVGA and VGA mode and the scan direction. As these pins can be also
configured with external pulls, all the GPIOs are considered optional with holes
in the array.
Example
-------
Example when connected to a omap2+ based device:
lcd0: display {
compatible = "sharp,ls037v7dw01";
power-supply = <&lcd_3v3>;
enable-gpios = <&gpio5 24 GPIO_ACTIVE_HIGH>; /* gpio152, lcd INI */
reset-gpios = <&gpio5 27 GPIO_ACTIVE_HIGH>; /* gpio155, lcd RESB */
mode-gpios = <&gpio5 26 GPIO_ACTIVE_HIGH /* gpio154, lcd MO */
&gpio1 2 GPIO_ACTIVE_HIGH /* gpio2, lcd LR */
&gpio1 3 GPIO_ACTIVE_HIGH>; /* gpio3, lcd UD */
port {
lcd_in: endpoint {
remote-endpoint = <&dpi_out>;
};
};
};
......@@ -109,3 +109,7 @@ Required properties:
Optional nodes:
- Video port for HDMI output
HDMI Endpoint optional properties:
- lanes: list of 8 pin numbers for the HDMI lanes: CLK+, CLK-, D0+, D0-,
D1+, D1-, D2+, D2-. (default: 0,1,2,3,4,5,6,7)
Texas Instruments OMAP5 Display Subsystem
=========================================
See Documentation/devicetree/bindings/video/ti,omap-dss.txt for generic
description about OMAP Display Subsystem bindings.
DSS Core
--------
Required properties:
- compatible: "ti,omap5-dss"
- reg: address and length of the register space
- ti,hwmods: "dss_core"
- clocks: handle to fclk
- clock-names: "fck"
Required nodes:
- DISPC
Optional nodes:
- DSS Submodules: RFBI, DSI, HDMI
- Video port for DPI output
DPI Endpoint required properties:
- data-lines: number of lines used
DISPC
-----
Required properties:
- compatible: "ti,omap5-dispc"
- reg: address and length of the register space
- ti,hwmods: "dss_dispc"
- interrupts: the DISPC interrupt
- clocks: handle to fclk
- clock-names: "fck"
RFBI
----
Required properties:
- compatible: "ti,omap5-rfbi"
- reg: address and length of the register space
- ti,hwmods: "dss_rfbi"
- clocks: handles to fclk and iclk
- clock-names: "fck", "ick"
Optional nodes:
- Video port for RFBI output
- RFBI controlled peripherals
DSI
---
Required properties:
- compatible: "ti,omap5-dsi"
- reg: addresses and lengths of the register spaces for 'proto', 'phy' and 'pll'
- reg-names: "proto", "phy", "pll"
- interrupts: the DSI interrupt line
- ti,hwmods: "dss_dsi1" or "dss_dsi2"
- vdd-supply: power supply for DSI
- clocks: handles to fclk and pll clock
- clock-names: "fck", "sys_clk"
Optional nodes:
- Video port for DSI output
- DSI controlled peripherals
DSI Endpoint required properties:
- lanes: list of pin numbers for the DSI lanes: CLK+, CLK-, DATA0+, DATA0-,
DATA1+, DATA1-, ...
HDMI
----
Required properties:
- compatible: "ti,omap5-hdmi"
- reg: addresses and lengths of the register spaces for 'wp', 'pll', 'phy',
'core'
- reg-names: "wp", "pll", "phy", "core"
- interrupts: the HDMI interrupt line
- ti,hwmods: "dss_hdmi"
- vdda-supply: vdda power supply
- clocks: handles to fclk and pll clock
- clock-names: "fck", "sys_clk"
Optional nodes:
- Video port for HDMI output
HDMI Endpoint optional properties:
- lanes: list of 8 pin numbers for the HDMI lanes: CLK+, CLK-, D0+, D0-,
D1+, D1-, D2+, D2-. (default: 0,1,2,3,4,5,6,7)
Toppoly TD028TTEC1 Panel
========================
Required properties:
- compatible: "toppoly,td028ttec1"
Optional properties:
- label: a symbolic name for the panel
Required nodes:
- Video port for DPI input
Example
-------
lcd-panel: td028ttec1@0 {
compatible = "toppoly,td028ttec1";
reg = <0>;
spi-max-frequency = <100000>;
spi-cpol;
spi-cpha;
label = "lcd";
port {
lcd_in: endpoint {
remote-endpoint = <&dpi_out>;
};
};
};
TPO TD043MTEA1 Panel
====================
Required properties:
- compatible: "tpo,td043mtea1"
- reset-gpios: panel reset gpio
Optional properties:
- label: a symbolic name for the panel
Required nodes:
- Video port for DPI input
Example
-------
lcd-panel: panel@0 {
compatible = "tpo,td043mtea1";
reg = <0>;
spi-max-frequency = <100000>;
spi-cpol;
spi-cpha;
label = "lcd";
reset-gpios = <&gpio7 7 0>;
port {
lcd_in: endpoint {
remote-endpoint = <&dpi_out>;
};
};
};
......@@ -279,6 +279,8 @@ static enum omapdss_version __init omap_display_get_version(void)
return OMAPDSS_VER_OMAP4;
else if (soc_is_omap54xx())
return OMAPDSS_VER_OMAP5;
else if (soc_is_am43xx())
return OMAPDSS_VER_AM43xx;
else
return OMAPDSS_VER_UNKNOWN;
}
......@@ -555,65 +557,9 @@ int omap_dss_reset(struct omap_hwmod *oh)
return r;
}
/* list of 'compatible' nodes to convert to omapdss specific */
static const char * const dss_compat_conv_list[] __initconst = {
"composite-connector",
"dvi-connector",
"hdmi-connector",
"panel-dpi",
"panel-dsi-cm",
"sony,acx565akm",
"svideo-connector",
"ti,tfp410",
"ti,tpd12s015",
};
/* prepend compatible string with "omapdss," */
static __init void omapdss_omapify_node(struct device_node *node,
const char *compat)
{
char *new_compat;
struct property *prop;
new_compat = kasprintf(GFP_KERNEL, "omapdss,%s", compat);
prop = kzalloc(sizeof(*prop), GFP_KERNEL);
if (!prop) {
pr_err("omapdss_omapify_node: kzalloc failed\n");
return;
}
prop->name = "compatible";
prop->value = new_compat;
prop->length = strlen(new_compat) + 1;
of_update_property(node, prop);
}
/*
* As omapdss panel drivers are omapdss specific, but we want to define the
* DT-data in generic manner, we convert the compatible strings of the panel
* nodes from "panel-foo" to "omapdss,panel-foo". This way we can have both
* correct DT data and omapdss specific drivers.
*
* When we get generic panel drivers to the kernel, this will be removed.
*/
void __init omapdss_early_init_of(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(dss_compat_conv_list); ++i) {
const char *compat = dss_compat_conv_list[i];
struct device_node *node = NULL;
while ((node = of_find_compatible_node(node, NULL, compat))) {
if (!of_device_is_available(node))
continue;
omapdss_omapify_node(node, compat);
}
}
}
struct device_node * __init omapdss_find_dss_of_node(void)
......@@ -632,6 +578,10 @@ struct device_node * __init omapdss_find_dss_of_node(void)
if (node)
return node;
node = of_find_compatible_node(NULL, NULL, "ti,omap5-dss");
if (node)
return node;
return NULL;
}
......
obj-$(CONFIG_OMAP2_VRFB) += vrfb.o
obj-$(CONFIG_OMAP2_DSS) += dss/
obj-y += dss/
obj-y += displays-new/
obj-$(CONFIG_FB_OMAP2) += omapfb/
......@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <drm/drm_edid.h>
......@@ -43,6 +44,8 @@ struct panel_drv_data {
struct device *dev;
struct omap_video_timings timings;
int hpd_gpio;
};
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
......@@ -161,7 +164,10 @@ static bool hdmic_detect(struct omap_dss_device *dssdev)
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
return in->ops.hdmi->detect(in);
if (gpio_is_valid(ddata->hpd_gpio))
return gpio_get_value_cansleep(ddata->hpd_gpio);
else
return in->ops.hdmi->detect(in);
}
static int hdmic_audio_enable(struct omap_dss_device *dssdev)
......@@ -288,6 +294,8 @@ static int hdmic_probe_pdata(struct platform_device *pdev)
pdata = dev_get_platdata(&pdev->dev);
ddata->hpd_gpio = -ENODEV;
in = omap_dss_find_output(pdata->source);
if (in == NULL) {
dev_err(&pdev->dev, "Failed to find video source\n");
......@@ -307,6 +315,14 @@ static int hdmic_probe_of(struct platform_device *pdev)
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct device_node *node = pdev->dev.of_node;
struct omap_dss_device *in;
int gpio;
/* HPD GPIO */
gpio = of_get_named_gpio(node, "hpd-gpios", 0);
if (gpio_is_valid(gpio))
ddata->hpd_gpio = gpio;
else
ddata->hpd_gpio = -ENODEV;
in = omapdss_of_find_source_for_first_ep(node);
if (IS_ERR(in)) {
......@@ -344,6 +360,13 @@ static int hdmic_probe(struct platform_device *pdev)
return -ENODEV;
}
if (gpio_is_valid(ddata->hpd_gpio)) {
r = devm_gpio_request_one(&pdev->dev, ddata->hpd_gpio,
GPIOF_DIR_IN, "hdmi_hpd");
if (r)
goto err_reg;
}
ddata->timings = hdmic_default_timings;
dssdev = &ddata->dssdev;
......
......@@ -13,9 +13,12 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
#include <video/of_display_timing.h>
struct panel_drv_data {
struct omap_dss_device dssdev;
......@@ -25,8 +28,10 @@ struct panel_drv_data {
struct omap_video_timings videomode;
/* used for non-DT boot, to be removed */
int backlight_gpio;
int enable_gpio;
struct gpio_desc *enable_gpio;
};
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
......@@ -70,15 +75,16 @@ static int panel_dpi_enable(struct omap_dss_device *dssdev)
if (omapdss_device_is_enabled(dssdev))
return 0;
in->ops.dpi->set_data_lines(in, ddata->data_lines);
if (ddata->data_lines)
in->ops.dpi->set_data_lines(in, ddata->data_lines);
in->ops.dpi->set_timings(in, &ddata->videomode);
r = in->ops.dpi->enable(in);
if (r)
return r;
if (gpio_is_valid(ddata->enable_gpio))
gpio_set_value_cansleep(ddata->enable_gpio, 1);
if (ddata->enable_gpio)
gpiod_set_value_cansleep(ddata->enable_gpio, 1);
if (gpio_is_valid(ddata->backlight_gpio))
gpio_set_value_cansleep(ddata->backlight_gpio, 1);
......@@ -96,8 +102,8 @@ static void panel_dpi_disable(struct omap_dss_device *dssdev)
if (!omapdss_device_is_enabled(dssdev))
return;
if (gpio_is_valid(ddata->enable_gpio))
gpio_set_value_cansleep(ddata->enable_gpio, 0);
if (ddata->enable_gpio)
gpiod_set_value_cansleep(ddata->enable_gpio, 0);
if (gpio_is_valid(ddata->backlight_gpio))
gpio_set_value_cansleep(ddata->backlight_gpio, 0);
......@@ -156,6 +162,7 @@ static int panel_dpi_probe_pdata(struct platform_device *pdev)
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct omap_dss_device *dssdev, *in;
struct videomode vm;
int r;
pdata = dev_get_platdata(&pdev->dev);
......@@ -176,9 +183,64 @@ static int panel_dpi_probe_pdata(struct platform_device *pdev)
dssdev = &ddata->dssdev;
dssdev->name = pdata->name;
ddata->enable_gpio = pdata->enable_gpio;
r = devm_gpio_request_one(&pdev->dev, pdata->enable_gpio,
GPIOF_OUT_INIT_LOW, "panel enable");
if (r)
goto err_gpio;
ddata->enable_gpio = gpio_to_desc(pdata->enable_gpio);
ddata->backlight_gpio = pdata->backlight_gpio;
return 0;
err_gpio:
omap_dss_put_device(ddata->in);
return r;
}
static int panel_dpi_probe_of(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct device_node *node = pdev->dev.of_node;
struct omap_dss_device *in;
int r;
struct display_timing timing;
struct videomode vm;
struct gpio_desc *gpio;
gpio = devm_gpiod_get(&pdev->dev, "enable");
if (IS_ERR(gpio)) {
if (PTR_ERR(gpio) != -ENOENT)
return PTR_ERR(gpio);
else
gpio = NULL;
} else {
gpiod_direction_output(gpio, 0);
}
ddata->enable_gpio = gpio;
ddata->backlight_gpio = -ENOENT;
r = of_get_display_timing(node, "panel-timing", &timing);
if (r) {
dev_err(&pdev->dev, "failed to get video timing\n");
return r;
}
videomode_from_timing(&timing, &vm);
videomode_to_omap_video_timings(&vm, &ddata->videomode);
in = omapdss_of_find_source_for_first_ep(node);
if (IS_ERR(in)) {
dev_err(&pdev->dev, "failed to find video source\n");
return PTR_ERR(in);
}
ddata->in = in;
return 0;
}
......@@ -198,17 +260,14 @@ static int panel_dpi_probe(struct platform_device *pdev)
r = panel_dpi_probe_pdata(pdev);
if (r)
return r;
} else if (pdev->dev.of_node) {
r = panel_dpi_probe_of(pdev);
if (r)
return r;
} else {
return -ENODEV;
}
if (gpio_is_valid(ddata->enable_gpio)) {
r = devm_gpio_request_one(&pdev->dev, ddata->enable_gpio,
GPIOF_OUT_INIT_LOW, "panel enable");
if (r)
goto err_gpio;
}
if (gpio_is_valid(ddata->backlight_gpio)) {
r = devm_gpio_request_one(&pdev->dev, ddata->backlight_gpio,
GPIOF_OUT_INIT_LOW, "panel backlight");
......@@ -254,12 +313,20 @@ static int __exit panel_dpi_remove(struct platform_device *pdev)
return 0;
}
static const struct of_device_id panel_dpi_of_match[] = {
{ .compatible = "omapdss,panel-dpi", },
{},
};
MODULE_DEVICE_TABLE(of, panel_dpi_of_match);
static struct platform_driver panel_dpi_driver = {
.probe = panel_dpi_probe,
.remove = __exit_p(panel_dpi_remove),
.driver = {
.name = "panel-dpi",
.owner = THIS_MODULE,
.of_match_table = panel_dpi_of_match,
},
};
......
......@@ -50,9 +50,10 @@ struct panel_drv_data {
struct omap_video_timings videomode;
int reset_gpio;
/* used for non-DT boot, to be removed */
int backlight_gpio;
int enable_gpio;
struct gpio_desc *enable_gpio;
};
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
......@@ -158,15 +159,16 @@ static int lb035q02_enable(struct omap_dss_device *dssdev)
if (omapdss_device_is_enabled(dssdev))
return 0;
in->ops.dpi->set_data_lines(in, ddata->data_lines);
if (ddata->data_lines)
in->ops.dpi->set_data_lines(in, ddata->data_lines);
in->ops.dpi->set_timings(in, &ddata->videomode);
r = in->ops.dpi->enable(in);
if (r)
return r;
if (gpio_is_valid(ddata->enable_gpio))
gpio_set_value_cansleep(ddata->enable_gpio, 1);
if (ddata->enable_gpio)
gpiod_set_value_cansleep(ddata->enable_gpio, 1);
if (gpio_is_valid(ddata->backlight_gpio))
gpio_set_value_cansleep(ddata->backlight_gpio, 1);
......@@ -184,8 +186,8 @@ static void lb035q02_disable(struct omap_dss_device *dssdev)
if (!omapdss_device_is_enabled(dssdev))
return;
if (gpio_is_valid(ddata->enable_gpio))
gpio_set_value_cansleep(ddata->enable_gpio, 0);
if (ddata->enable_gpio)
gpiod_set_value_cansleep(ddata->enable_gpio, 0);
if (gpio_is_valid(ddata->backlight_gpio))
gpio_set_value_cansleep(ddata->backlight_gpio, 0);
......@@ -243,6 +245,7 @@ static int lb035q02_probe_pdata(struct spi_device *spi)
const struct panel_lb035q02_platform_data *pdata;
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
struct omap_dss_device *dssdev, *in;
int r;
pdata = dev_get_platdata(&spi->dev);
......@@ -260,9 +263,47 @@ static int lb035q02_probe_pdata(struct spi_device *spi)
dssdev = &ddata->dssdev;
dssdev->name = pdata->name;
ddata->enable_gpio = pdata->enable_gpio;
r = devm_gpio_request_one(&spi->dev, pdata->enable_gpio,
GPIOF_OUT_INIT_LOW, "panel enable");
if (r)
goto err_gpio;
ddata->enable_gpio = gpio_to_desc(pdata->enable_gpio);
ddata->backlight_gpio = pdata->backlight_gpio;
return 0;
err_gpio:
omap_dss_put_device(ddata->in);
return r;
}
static int lb035q02_probe_of(struct spi_device *spi)
{
struct device_node *node = spi->dev.of_node;
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
struct omap_dss_device *in;
struct gpio_desc *gpio;
gpio = devm_gpiod_get(&spi->dev, "enable");
if (IS_ERR(gpio)) {
dev_err(&spi->dev, "failed to parse enable gpio\n");
return PTR_ERR(gpio);
} else {
gpiod_direction_output(gpio, 0);
ddata->enable_gpio = gpio;
}
ddata->backlight_gpio = -ENOENT;
in = omapdss_of_find_source_for_first_ep(node);
if (IS_ERR(in)) {
dev_err(&spi->dev, "failed to find video source\n");
return PTR_ERR(in);
}
ddata->in = in;
return 0;
}
......@@ -284,17 +325,14 @@ static int lb035q02_panel_spi_probe(struct spi_device *spi)
r = lb035q02_probe_pdata(spi);
if (r)
return r;
} else if (spi->dev.of_node) {
r = lb035q02_probe_of(spi);
if (r)
return r;
} else {
return -ENODEV;
}
if (gpio_is_valid(ddata->enable_gpio)) {
r = devm_gpio_request_one(&spi->dev, ddata->enable_gpio,
GPIOF_OUT_INIT_LOW, "panel enable");
if (r)
goto err_gpio;
}
if (gpio_is_valid(ddata->backlight_gpio)) {
r = devm_gpio_request_one(&spi->dev, ddata->backlight_gpio,
GPIOF_OUT_INIT_LOW, "panel backlight");
......@@ -342,17 +380,26 @@ static int lb035q02_panel_spi_remove(struct spi_device *spi)
return 0;
}
static const struct of_device_id lb035q02_of_match[] = {
{ .compatible = "omapdss,lgphilips,lb035q02", },
{},
};
MODULE_DEVICE_TABLE(of, lb035q02_of_match);
static struct spi_driver lb035q02_spi_driver = {
.probe = lb035q02_panel_spi_probe,
.remove = lb035q02_panel_spi_remove,
.driver = {
.name = "panel_lgphilips_lb035q02",
.owner = THIS_MODULE,
.of_match_table = lb035q02_of_match,
},
};
module_spi_driver(lb035q02_spi_driver);
MODULE_ALIAS("spi:lgphilips,lb035q02");
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
MODULE_DESCRIPTION("LG.Philips LB035Q02 LCD Panel driver");
MODULE_LICENSE("GPL");
......@@ -16,6 +16,7 @@
#include <linux/spi/spi.h>
#include <linux/fb.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
......@@ -156,7 +157,8 @@ static int nec_8048_enable(struct omap_dss_device *dssdev)
if (omapdss_device_is_enabled(dssdev))
return 0;
in->ops.dpi->set_data_lines(in, ddata->data_lines);
if (ddata->data_lines)
in->ops.dpi->set_data_lines(in, ddata->data_lines);
in->ops.dpi->set_timings(in, &ddata->videomode);
r = in->ops.dpi->enable(in);
......@@ -258,6 +260,34 @@ static int nec_8048_probe_pdata(struct spi_device *spi)
return 0;
}
static int nec_8048_probe_of(struct spi_device *spi)
{
struct device_node *node = spi->dev.of_node;
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
struct omap_dss_device *in;
int gpio;
gpio = of_get_named_gpio(node, "reset-gpios", 0);
if (!gpio_is_valid(gpio)) {
dev_err(&spi->dev, "failed to parse enable gpio\n");
return gpio;
}
ddata->res_gpio = gpio;
/* XXX the panel spec doesn't mention any QVGA pin?? */
ddata->qvga_gpio = -ENOENT;
in = omapdss_of_find_source_for_first_ep(node);
if (IS_ERR(in)) {
dev_err(&spi->dev, "failed to find video source\n");
return PTR_ERR(in);
}
ddata->in = in;
return 0;
}
static int nec_8048_probe(struct spi_device *spi)
{
struct panel_drv_data *ddata;
......@@ -289,6 +319,10 @@ static int nec_8048_probe(struct spi_device *spi)
r = nec_8048_probe_pdata(spi);
if (r)
return r;
} else if (spi->dev.of_node) {
r = nec_8048_probe_of(spi);
if (r)
return r;
} else {
return -ENODEV;
}
......@@ -377,11 +411,19 @@ static SIMPLE_DEV_PM_OPS(nec_8048_pm_ops, nec_8048_suspend,
#define NEC_8048_PM_OPS NULL
#endif
static const struct of_device_id nec_8048_of_match[] = {
{ .compatible = "omapdss,nec,nl8048hl11", },
{},
};
MODULE_DEVICE_TABLE(of, nec_8048_of_match);
static struct spi_driver nec_8048_driver = {
.driver = {
.name = "panel-nec-nl8048hl11",
.owner = THIS_MODULE,
.pm = NEC_8048_PM_OPS,
.of_match_table = nec_8048_of_match,
},
.probe = nec_8048_probe,
.remove = nec_8048_remove,
......@@ -389,6 +431,7 @@ static struct spi_driver nec_8048_driver = {
module_spi_driver(nec_8048_driver);
MODULE_ALIAS("spi:nec,nl8048hl11");
MODULE_AUTHOR("Erik Gilling <konkers@android.com>");
MODULE_DESCRIPTION("NEC-NL8048HL11 Driver");
MODULE_LICENSE("GPL");
......@@ -12,25 +12,28 @@
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/regulator/consumer.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
struct panel_drv_data {
struct omap_dss_device dssdev;
struct omap_dss_device *in;
struct regulator *vcc;
int data_lines;
struct omap_video_timings videomode;
int resb_gpio;
int ini_gpio;
int mo_gpio;
int lr_gpio;
int ud_gpio;
struct gpio_desc *resb_gpio; /* low = reset active min 20 us */
struct gpio_desc *ini_gpio; /* high = power on */
struct gpio_desc *mo_gpio; /* low = 480x640, high = 240x320 */
struct gpio_desc *lr_gpio; /* high = conventional horizontal scanning */
struct gpio_desc *ud_gpio; /* high = conventional vertical scanning */
};
static const struct omap_video_timings sharp_ls_timings = {
......@@ -95,21 +98,30 @@ static int sharp_ls_enable(struct omap_dss_device *dssdev)
if (omapdss_device_is_enabled(dssdev))
return 0;
in->ops.dpi->set_data_lines(in, ddata->data_lines);
if (ddata->data_lines)
in->ops.dpi->set_data_lines(in, ddata->data_lines);
in->ops.dpi->set_timings(in, &ddata->videomode);
if (ddata->vcc) {
r = regulator_enable(ddata->vcc);
if (r != 0)
return r;
}
r = in->ops.dpi->enable(in);
if (r)
if (r) {
regulator_disable(ddata->vcc);
return r;
}
/* wait couple of vsyncs until enabling the LCD */
msleep(50);
if (gpio_is_valid(ddata->resb_gpio))
gpio_set_value_cansleep(ddata->resb_gpio, 1);
if (ddata->resb_gpio)
gpiod_set_value_cansleep(ddata->resb_gpio, 1);
if (gpio_is_valid(ddata->ini_gpio))
gpio_set_value_cansleep(ddata->ini_gpio, 1);
if (ddata->ini_gpio)
gpiod_set_value_cansleep(ddata->ini_gpio, 1);
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
......@@ -124,11 +136,11 @@ static void sharp_ls_disable(struct omap_dss_device *dssdev)
if (!omapdss_device_is_enabled(dssdev))
return;
if (gpio_is_valid(ddata->ini_gpio))
gpio_set_value_cansleep(ddata->ini_gpio, 0);
if (ddata->ini_gpio)
gpiod_set_value_cansleep(ddata->ini_gpio, 0);
if (gpio_is_valid(ddata->resb_gpio))
gpio_set_value_cansleep(ddata->resb_gpio, 0);
if (ddata->resb_gpio)
gpiod_set_value_cansleep(ddata->resb_gpio, 0);
/* wait at least 5 vsyncs after disabling the LCD */
......@@ -136,6 +148,9 @@ static void sharp_ls_disable(struct omap_dss_device *dssdev)
in->ops.dpi->disable(in);
if (ddata->vcc)
regulator_disable(ddata->vcc);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
......@@ -182,11 +197,32 @@ static struct omap_dss_driver sharp_ls_ops = {
.get_resolution = omapdss_default_get_resolution,
};
static int sharp_ls_get_gpio(struct device *dev, int gpio, unsigned long flags,
char *desc, struct gpio_desc **gpiod)
{
struct gpio_desc *gd;
int r;
*gpiod = NULL;
r = devm_gpio_request_one(dev, gpio, flags, desc);
if (r)
return r == -ENOENT ? 0 : r;
gd = gpio_to_desc(gpio);
if (IS_ERR(gd))
return PTR_ERR(gd) == -ENOENT ? 0 : PTR_ERR(gd);
*gpiod = gd;
return 0;
}
static int sharp_ls_probe_pdata(struct platform_device *pdev)
{
const struct panel_sharp_ls037v7dw01_platform_data *pdata;
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct omap_dss_device *dssdev, *in;
int r;
pdata = dev_get_platdata(&pdev->dev);
......@@ -204,11 +240,95 @@ static int sharp_ls_probe_pdata(struct platform_device *pdev)
dssdev = &ddata->dssdev;
dssdev->name = pdata->name;
ddata->resb_gpio = pdata->resb_gpio;
ddata->ini_gpio = pdata->ini_gpio;
ddata->mo_gpio = pdata->mo_gpio;
ddata->lr_gpio = pdata->lr_gpio;
ddata->ud_gpio = pdata->ud_gpio;
r = sharp_ls_get_gpio(&pdev->dev, pdata->mo_gpio, GPIOF_OUT_INIT_LOW,
"lcd MO", &ddata->mo_gpio);
if (r)
return r;
r = sharp_ls_get_gpio(&pdev->dev, pdata->lr_gpio, GPIOF_OUT_INIT_HIGH,
"lcd LR", &ddata->lr_gpio);
if (r)
return r;
r = sharp_ls_get_gpio(&pdev->dev, pdata->ud_gpio, GPIOF_OUT_INIT_HIGH,
"lcd UD", &ddata->ud_gpio);
if (r)
return r;
r = sharp_ls_get_gpio(&pdev->dev, pdata->resb_gpio, GPIOF_OUT_INIT_LOW,
"lcd RESB", &ddata->resb_gpio);
if (r)
return r;
r = sharp_ls_get_gpio(&pdev->dev, pdata->ini_gpio, GPIOF_OUT_INIT_LOW,
"lcd INI", &ddata->ini_gpio);
if (r)
return r;
return 0;
}
static int sharp_ls_get_gpio_of(struct device *dev, int index, int val,
const char *desc, struct gpio_desc **gpiod)
{
struct gpio_desc *gd;
int r;
*gpiod = NULL;
gd = devm_gpiod_get_index(dev, desc, index);
if (IS_ERR(gd))
return PTR_ERR(gd) == -ENOENT ? 0 : PTR_ERR(gd);
r = gpiod_direction_output(gd, val);
if (r)
return r;
*gpiod = gd;
return 0;
}
static int sharp_ls_probe_of(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct device_node *node = pdev->dev.of_node;
struct omap_dss_device *in;
int r;
ddata->vcc = devm_regulator_get(&pdev->dev, "envdd");
if (IS_ERR(ddata->vcc)) {
dev_err(&pdev->dev, "failed to get regulator\n");
return PTR_ERR(ddata->vcc);
}
/* lcd INI */
r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "enable", &ddata->ini_gpio);
if (r)
return r;
/* lcd RESB */
r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "reset", &ddata->resb_gpio);
if (r)
return r;
/* lcd MO */
r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "mode", &ddata->mo_gpio);
if (r)
return r;
/* lcd LR */
r = sharp_ls_get_gpio_of(&pdev->dev, 1, 1, "mode", &ddata->lr_gpio);
if (r)
return r;
/* lcd UD */
r = sharp_ls_get_gpio_of(&pdev->dev, 2, 1, "mode", &ddata->ud_gpio);
if (r)
return r;
in = omapdss_of_find_source_for_first_ep(node);
if (IS_ERR(in)) {
dev_err(&pdev->dev, "failed to find video source\n");
return PTR_ERR(in);
}
ddata->in = in;
return 0;
}
......@@ -229,45 +349,14 @@ static int sharp_ls_probe(struct platform_device *pdev)
r = sharp_ls_probe_pdata(pdev);
if (r)
return r;
} else if (pdev->dev.of_node) {
r = sharp_ls_probe_of(pdev);
if (r)
return r;
} else {
return -ENODEV;
}
if (gpio_is_valid(ddata->mo_gpio)) {
r = devm_gpio_request_one(&pdev->dev, ddata->mo_gpio,
GPIOF_OUT_INIT_LOW, "lcd MO");
if (r)
goto err_gpio;
}
if (gpio_is_valid(ddata->lr_gpio)) {
r = devm_gpio_request_one(&pdev->dev, ddata->lr_gpio,
GPIOF_OUT_INIT_HIGH, "lcd LR");
if (r)
goto err_gpio;
}
if (gpio_is_valid(ddata->ud_gpio)) {
r = devm_gpio_request_one(&pdev->dev, ddata->ud_gpio,
GPIOF_OUT_INIT_HIGH, "lcd UD");
if (r)
goto err_gpio;
}
if (gpio_is_valid(ddata->resb_gpio)) {
r = devm_gpio_request_one(&pdev->dev, ddata->resb_gpio,
GPIOF_OUT_INIT_LOW, "lcd RESB");
if (r)
goto err_gpio;
}
if (gpio_is_valid(ddata->ini_gpio)) {
r = devm_gpio_request_one(&pdev->dev, ddata->ini_gpio,
GPIOF_OUT_INIT_LOW, "lcd INI");
if (r)
goto err_gpio;
}
ddata->videomode = sharp_ls_timings;
dssdev = &ddata->dssdev;
......@@ -287,7 +376,6 @@ static int sharp_ls_probe(struct platform_device *pdev)
return 0;
err_reg:
err_gpio:
omap_dss_put_device(ddata->in);
return r;
}
......@@ -308,12 +396,20 @@ static int __exit sharp_ls_remove(struct platform_device *pdev)
return 0;
}
static const struct of_device_id sharp_ls_of_match[] = {
{ .compatible = "omapdss,sharp,ls037v7dw01", },
{},
};
MODULE_DEVICE_TABLE(of, sharp_ls_of_match);
static struct platform_driver sharp_ls_driver = {
.probe = sharp_ls_probe,
.remove = __exit_p(sharp_ls_remove),
.driver = {
.name = "panel-sharp-ls037v7dw01",
.owner = THIS_MODULE,
.of_match_table = sharp_ls_of_match,
},
};
......
......@@ -206,7 +206,8 @@ static int td028ttec1_panel_enable(struct omap_dss_device *dssdev)
if (omapdss_device_is_enabled(dssdev))
return 0;
in->ops.dpi->set_data_lines(in, ddata->data_lines);
if (ddata->data_lines)
in->ops.dpi->set_data_lines(in, ddata->data_lines);
in->ops.dpi->set_timings(in, &ddata->videomode);
r = in->ops.dpi->enable(in);
......@@ -389,6 +390,23 @@ static int td028ttec1_panel_probe_pdata(struct spi_device *spi)
return 0;
}
static int td028ttec1_probe_of(struct spi_device *spi)
{
struct device_node *node = spi->dev.of_node;
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
struct omap_dss_device *in;
in = omapdss_of_find_source_for_first_ep(node);
if (IS_ERR(in)) {
dev_err(&spi->dev, "failed to find video source\n");
return PTR_ERR(in);
}
ddata->in = in;
return 0;
}
static int td028ttec1_panel_probe(struct spi_device *spi)
{
struct panel_drv_data *ddata;
......@@ -418,6 +436,10 @@ static int td028ttec1_panel_probe(struct spi_device *spi)
r = td028ttec1_panel_probe_pdata(spi);
if (r)
return r;
} else if (spi->dev.of_node) {
r = td028ttec1_probe_of(spi);
if (r)
return r;
} else {
return -ENODEV;
}
......@@ -463,6 +485,13 @@ static int td028ttec1_panel_remove(struct spi_device *spi)
return 0;
}
static const struct of_device_id td028ttec1_of_match[] = {
{ .compatible = "omapdss,toppoly,td028ttec1", },
{},
};
MODULE_DEVICE_TABLE(of, td028ttec1_of_match);
static struct spi_driver td028ttec1_spi_driver = {
.probe = td028ttec1_panel_probe,
.remove = td028ttec1_panel_remove,
......@@ -470,11 +499,13 @@ static struct spi_driver td028ttec1_spi_driver = {
.driver = {
.name = "panel-tpo-td028ttec1",
.owner = THIS_MODULE,
.of_match_table = td028ttec1_of_match,
},
};
module_spi_driver(td028ttec1_spi_driver);
MODULE_ALIAS("spi:toppoly,td028ttec1");
MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
MODULE_DESCRIPTION("Toppoly TD028TTEC1 panel driver");
MODULE_LICENSE("GPL");
......@@ -17,6 +17,7 @@
#include <linux/gpio.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/of_gpio.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
......@@ -376,7 +377,8 @@ static int tpo_td043_enable(struct omap_dss_device *dssdev)
if (omapdss_device_is_enabled(dssdev))
return 0;
in->ops.dpi->set_data_lines(in, ddata->data_lines);
if (ddata->data_lines)
in->ops.dpi->set_data_lines(in, ddata->data_lines);
in->ops.dpi->set_timings(in, &ddata->videomode);
r = in->ops.dpi->enable(in);
......@@ -489,6 +491,31 @@ static int tpo_td043_probe_pdata(struct spi_device *spi)
return 0;
}
static int tpo_td043_probe_of(struct spi_device *spi)
{
struct device_node *node = spi->dev.of_node;
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
struct omap_dss_device *in;
int gpio;
gpio = of_get_named_gpio(node, "reset-gpios", 0);
if (!gpio_is_valid(gpio)) {
dev_err(&spi->dev, "failed to parse enable gpio\n");
return gpio;
}
ddata->nreset_gpio = gpio;
in = omapdss_of_find_source_for_first_ep(node);
if (IS_ERR(in)) {
dev_err(&spi->dev, "failed to find video source\n");
return PTR_ERR(in);
}
ddata->in = in;
return 0;
}
static int tpo_td043_probe(struct spi_device *spi)
{
struct panel_drv_data *ddata;
......@@ -518,6 +545,10 @@ static int tpo_td043_probe(struct spi_device *spi)
r = tpo_td043_probe_pdata(spi);
if (r)
return r;
} else if (spi->dev.of_node) {
r = tpo_td043_probe_of(spi);
if (r)
return r;
} else {
return -ENODEV;
}
......@@ -629,11 +660,19 @@ static int tpo_td043_spi_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(tpo_td043_spi_pm,
tpo_td043_spi_suspend, tpo_td043_spi_resume);
static const struct of_device_id tpo_td043_of_match[] = {
{ .compatible = "omapdss,tpo,td043mtea1", },
{},
};
MODULE_DEVICE_TABLE(of, tpo_td043_of_match);
static struct spi_driver tpo_td043_spi_driver = {
.driver = {
.name = "panel-tpo-td043mtea1",
.owner = THIS_MODULE,
.pm = &tpo_td043_spi_pm,
.of_match_table = tpo_td043_of_match,
},
.probe = tpo_td043_probe,
.remove = tpo_td043_remove,
......@@ -641,6 +680,7 @@ static struct spi_driver tpo_td043_spi_driver = {
module_spi_driver(tpo_td043_spi_driver);
MODULE_ALIAS("spi:tpo,td043mtea1");
MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>");
MODULE_DESCRIPTION("TPO TD043MTEA1 LCD Driver");
MODULE_LICENSE("GPL");
config OMAP2_DSS_INIT
bool
menuconfig OMAP2_DSS
tristate "OMAP2+ Display Subsystem support"
select VIDEOMODE_HELPERS
select OMAP2_DSS_INIT
help
OMAP2+ Display Subsystem support.
......@@ -59,16 +63,32 @@ config OMAP2_DSS_VENC
help
OMAP Video Encoder support for S-Video and composite TV-out.
config OMAP2_DSS_HDMI_COMMON
bool
config OMAP4_DSS_HDMI
bool "HDMI support"
bool "HDMI support for OMAP4"
default y
select OMAP2_DSS_HDMI_COMMON
help
HDMI Interface. This adds the High Definition Multimedia Interface.
See http://www.hdmi.org/ for HDMI specification.
HDMI support for OMAP4 based SoCs.
config OMAP4_DSS_HDMI_AUDIO
bool
config OMAP5_DSS_HDMI
bool "HDMI support for OMAP5"
default n
select OMAP2_DSS_HDMI_COMMON
help
HDMI Interface for OMAP5 and similar cores. This adds the High
Definition Multimedia Interface. See http://www.hdmi.org/ for HDMI
specification.
config OMAP5_DSS_HDMI_AUDIO
depends on OMAP5_DSS_HDMI
bool
config OMAP2_DSS_SDI
bool "SDI support"
default n
......
obj-$(CONFIG_OMAP2_DSS_INIT) += omapdss-boot-init.o
obj-$(CONFIG_OMAP2_DSS) += omapdss.o
# Core DSS files
omapdss-y := core.o dss.o dss_features.o dispc.o dispc_coefs.o display.o \
......@@ -10,6 +11,8 @@ omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o
omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o
omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o
omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o
omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi4.o hdmi_common.o hdmi_wp.o hdmi_pll.o \
hdmi_phy.o hdmi4_core.o
omapdss-$(CONFIG_OMAP2_DSS_HDMI_COMMON) += hdmi_common.o hdmi_wp.o hdmi_pll.o \
hdmi_phy.o
omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi4.o hdmi4_core.o
omapdss-$(CONFIG_OMAP5_DSS_HDMI) += hdmi5.o hdmi5_core.o
ccflags-$(CONFIG_OMAP2_DSS_DEBUG) += -DDEBUG
......@@ -268,6 +268,9 @@ static int (*dss_output_drv_reg_funcs[])(void) __initdata = {
#ifdef CONFIG_OMAP4_DSS_HDMI
hdmi4_init_platform_driver,
#endif
#ifdef CONFIG_OMAP5_DSS_HDMI
hdmi5_init_platform_driver,
#endif
};
static void (*dss_output_drv_unreg_funcs[])(void) __exitdata = {
......@@ -289,6 +292,9 @@ static void (*dss_output_drv_unreg_funcs[])(void) __exitdata = {
#ifdef CONFIG_OMAP4_DSS_HDMI
hdmi4_uninit_platform_driver,
#endif
#ifdef CONFIG_OMAP5_DSS_HDMI
hdmi5_uninit_platform_driver,
#endif
};
static bool dss_output_drv_loaded[ARRAY_SIZE(dss_output_drv_reg_funcs)];
......
......@@ -2577,9 +2577,9 @@ int dispc_ovl_setup(enum omap_plane plane, const struct omap_overlay_info *oi,
channel = dispc_ovl_get_channel_out(plane);
DSSDBG("dispc_ovl_setup %d, pa %x, pa_uv %x, sw %d, %d,%d, %dx%d -> "
"%dx%d, cmode %x, rot %d, mir %d, chan %d repl %d\n",
plane, oi->paddr, oi->p_uv_addr, oi->screen_width, oi->pos_x,
DSSDBG("dispc_ovl_setup %d, pa %pad, pa_uv %pad, sw %d, %d,%d, %dx%d ->"
" %dx%d, cmode %x, rot %d, mir %d, chan %d repl %d\n",
plane, &oi->paddr, &oi->p_uv_addr, oi->screen_width, oi->pos_x,
oi->pos_y, oi->width, oi->height, oi->out_width, oi->out_height,
oi->color_mode, oi->rotation, oi->mirror, channel, replication);
......@@ -2945,13 +2945,13 @@ static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw,
BUG();
}
l = dispc_read_reg(DISPC_POL_FREQ(channel));
l |= FLD_VAL(onoff, 17, 17);
l |= FLD_VAL(rf, 16, 16);
l |= FLD_VAL(de_level, 15, 15);
l |= FLD_VAL(ipc, 14, 14);
l |= FLD_VAL(hsync_level, 13, 13);
l |= FLD_VAL(vsync_level, 12, 12);
l = FLD_VAL(onoff, 17, 17) |
FLD_VAL(rf, 16, 16) |
FLD_VAL(de_level, 15, 15) |
FLD_VAL(ipc, 14, 14) |
FLD_VAL(hsync_level, 13, 13) |
FLD_VAL(vsync_level, 12, 12);
dispc_write_reg(DISPC_POL_FREQ(channel), l);
}
......@@ -3656,6 +3656,7 @@ static int __init dispc_init_features(struct platform_device *pdev)
case OMAPDSS_VER_OMAP34xx_ES3:
case OMAPDSS_VER_OMAP3630:
case OMAPDSS_VER_AM35xx:
case OMAPDSS_VER_AM43xx:
src = &omap34xx_rev3_0_dispc_feats;
break;
......@@ -3829,6 +3830,7 @@ static const struct of_device_id dispc_of_match[] = {
{ .compatible = "ti,omap2-dispc", },
{ .compatible = "ti,omap3-dispc", },
{ .compatible = "ti,omap4-dispc", },
{ .compatible = "ti,omap5-dispc", },
{},
};
......
......@@ -67,6 +67,7 @@ static struct platform_device *dpi_get_dsidev(enum omap_channel channel)
case OMAPDSS_VER_OMAP34xx_ES3:
case OMAPDSS_VER_OMAP3630:
case OMAPDSS_VER_AM35xx:
case OMAPDSS_VER_AM43xx:
return NULL;
case OMAPDSS_VER_OMAP4430_ES1:
......@@ -103,6 +104,8 @@ static enum omap_dss_clk_source dpi_get_alt_clk_src(enum omap_channel channel)
return OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC;
case OMAP_DSS_CHANNEL_LCD2:
return OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC;
case OMAP_DSS_CHANNEL_LCD3:
return OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC;
default:
/* this shouldn't happen */
WARN_ON(1);
......@@ -595,6 +598,7 @@ static enum omap_channel dpi_get_channel(void)
case OMAPDSS_VER_OMAP34xx_ES3:
case OMAPDSS_VER_OMAP3630:
case OMAPDSS_VER_AM35xx:
case OMAPDSS_VER_AM43xx:
return OMAP_DSS_CHANNEL_LCD;
case OMAPDSS_VER_OMAP4430_ES1:
......
......@@ -1161,6 +1161,7 @@ static int dsi_regulator_init(struct platform_device *dsidev)
{
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
struct regulator *vdds_dsi;
int r;
if (dsi->vdds_dsi_reg != NULL)
return 0;
......@@ -1173,6 +1174,15 @@ static int dsi_regulator_init(struct platform_device *dsidev)
return PTR_ERR(vdds_dsi);
}
if (regulator_can_change_voltage(vdds_dsi)) {
r = regulator_set_voltage(vdds_dsi, 1800000, 1800000);
if (r) {
devm_regulator_put(vdds_dsi);
DSSERR("can't set the DSI regulator voltage\n");
return r;
}
}
dsi->vdds_dsi_reg = vdds_dsi;
return 0;
......@@ -5122,6 +5132,7 @@ static enum omap_channel dsi_get_channel(int module_id)
{
switch (omapdss_get_version()) {
case OMAPDSS_VER_OMAP24xx:
case OMAPDSS_VER_AM43xx:
DSSWARN("DSI not supported\n");
return OMAP_DSS_CHANNEL_LCD;
......@@ -5723,9 +5734,16 @@ static const struct dsi_module_id_data dsi_of_data_omap4[] = {
{ },
};
static const struct dsi_module_id_data dsi_of_data_omap5[] = {
{ .address = 0x58004000, .id = 0, },
{ .address = 0x58009000, .id = 1, },
{ },
};
static const struct of_device_id dsi_of_match[] = {
{ .compatible = "ti,omap3-dsi", .data = dsi_of_data_omap3, },
{ .compatible = "ti,omap4-dsi", .data = dsi_of_data_omap4, },
{ .compatible = "ti,omap5-dsi", .data = dsi_of_data_omap5, },
{},
};
......
......@@ -728,6 +728,13 @@ static const struct dss_features omap54xx_dss_feats __initconst = {
.dpi_select_source = &dss_dpi_select_source_omap5,
};
static const struct dss_features am43xx_dss_feats __initconst = {
.fck_div_max = 0,
.dss_fck_multiplier = 0,
.parent_clk_name = NULL,
.dpi_select_source = &dss_dpi_select_source_omap2_omap3,
};
static int __init dss_init_features(struct platform_device *pdev)
{
const struct dss_features *src;
......@@ -764,6 +771,10 @@ static int __init dss_init_features(struct platform_device *pdev)
src = &omap54xx_dss_feats;
break;
case OMAPDSS_VER_AM43xx:
src = &am43xx_dss_feats;
break;
default:
return -ENODEV;
}
......@@ -784,12 +795,8 @@ static int __init dss_init_ports(struct platform_device *pdev)
return 0;
port = omapdss_of_get_next_port(parent, NULL);
if (!port) {
#ifdef CONFIG_OMAP2_DSS_DPI
dpi_init_port(pdev, parent);
#endif
if (!port)
return 0;
}
do {
u32 reg;
......@@ -813,7 +820,7 @@ static int __init dss_init_ports(struct platform_device *pdev)
return 0;
}
static void dss_uninit_ports(void)
static void __exit dss_uninit_ports(void)
{
#ifdef CONFIG_OMAP2_DSS_DPI
dpi_uninit_port();
......@@ -946,6 +953,7 @@ static const struct of_device_id dss_of_match[] = {
{ .compatible = "ti,omap2-dss", },
{ .compatible = "ti,omap3-dss", },
{ .compatible = "ti,omap4-dss", },
{ .compatible = "ti,omap5-dss", },
{},
};
......
......@@ -419,6 +419,9 @@ void venc_uninit_platform_driver(void) __exit;
int hdmi4_init_platform_driver(void) __init;
void hdmi4_uninit_platform_driver(void) __exit;
int hdmi5_init_platform_driver(void) __init;
void hdmi5_uninit_platform_driver(void) __exit;
/* RFBI */
int rfbi_init_platform_driver(void) __init;
void rfbi_uninit_platform_driver(void) __exit;
......
......@@ -93,6 +93,17 @@ static const struct dss_reg_field omap3_dss_reg_fields[] = {
[FEAT_REG_DSIPLL_REGM_DSI] = { 26, 23 },
};
static const struct dss_reg_field am43xx_dss_reg_fields[] = {
[FEAT_REG_FIRHINC] = { 12, 0 },
[FEAT_REG_FIRVINC] = { 28, 16 },
[FEAT_REG_FIFOLOWTHRESHOLD] = { 11, 0 },
[FEAT_REG_FIFOHIGHTHRESHOLD] = { 27, 16 },
[FEAT_REG_FIFOSIZE] = { 10, 0 },
[FEAT_REG_HORIZONTALACCU] = { 9, 0 },
[FEAT_REG_VERTICALACCU] = { 25, 16 },
[FEAT_REG_DISPC_CLK_SWITCH] = { 0, 0 },
};
static const struct dss_reg_field omap4_dss_reg_fields[] = {
[FEAT_REG_FIRHINC] = { 12, 0 },
[FEAT_REG_FIRVINC] = { 28, 16 },
......@@ -149,6 +160,11 @@ static const enum omap_display_type omap3630_dss_supported_displays[] = {
OMAP_DISPLAY_TYPE_VENC,
};
static const enum omap_display_type am43xx_dss_supported_displays[] = {
/* OMAP_DSS_CHANNEL_LCD */
OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI,
};
static const enum omap_display_type omap4_dss_supported_displays[] = {
/* OMAP_DSS_CHANNEL_LCD */
OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_DSI,
......@@ -200,6 +216,11 @@ static const enum omap_dss_output_id omap3630_dss_supported_outputs[] = {
OMAP_DSS_OUTPUT_VENC,
};
static const enum omap_dss_output_id am43xx_dss_supported_outputs[] = {
/* OMAP_DSS_CHANNEL_LCD */
OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI,
};
static const enum omap_dss_output_id omap4_dss_supported_outputs[] = {
/* OMAP_DSS_CHANNEL_LCD */
OMAP_DSS_OUTPUT_DBI | OMAP_DSS_OUTPUT_DSI1,
......@@ -444,6 +465,13 @@ static const struct dss_param_range omap3_dss_param_range[] = {
[FEAT_PARAM_LINEWIDTH] = { 1, 1024 },
};
static const struct dss_param_range am43xx_dss_param_range[] = {
[FEAT_PARAM_DSS_FCK] = { 0, 200000000 },
[FEAT_PARAM_DSS_PCD] = { 2, 255 },
[FEAT_PARAM_DOWNSCALE] = { 1, 4 },
[FEAT_PARAM_LINEWIDTH] = { 1, 1024 },
};
static const struct dss_param_range omap4_dss_param_range[] = {
[FEAT_PARAM_DSS_FCK] = { 0, 186000000 },
[FEAT_PARAM_DSS_PCD] = { 1, 255 },
......@@ -520,6 +548,21 @@ static const enum dss_feat_id am35xx_dss_feat_list[] = {
FEAT_OMAP3_DSI_FIFO_BUG,
};
static const enum dss_feat_id am43xx_dss_feat_list[] = {
FEAT_LCDENABLEPOL,
FEAT_LCDENABLESIGNAL,
FEAT_PCKFREEENABLE,
FEAT_FUNCGATED,
FEAT_LINEBUFFERSPLIT,
FEAT_ROWREPEATENABLE,
FEAT_RESIZECONF,
FEAT_CPR,
FEAT_PRELOAD,
FEAT_FIR_COEF_V,
FEAT_ALPHA_FIXED_ZORDER,
FEAT_FIFO_MERGE,
};
static const enum dss_feat_id omap3630_dss_feat_list[] = {
FEAT_LCDENABLEPOL,
FEAT_LCDENABLESIGNAL,
......@@ -595,6 +638,7 @@ static const enum dss_feat_id omap4_dss_feat_list[] = {
static const enum dss_feat_id omap5_dss_feat_list[] = {
FEAT_MGR_LCD2,
FEAT_MGR_LCD3,
FEAT_CORE_CLK_DIV,
FEAT_LCD_CLK_SRC,
FEAT_DSI_DCS_CMD_CONFIG_VC,
......@@ -682,6 +726,26 @@ static const struct omap_dss_features am35xx_dss_features = {
.burst_size_unit = 8,
};
static const struct omap_dss_features am43xx_dss_features = {
.reg_fields = am43xx_dss_reg_fields,
.num_reg_fields = ARRAY_SIZE(am43xx_dss_reg_fields),
.features = am43xx_dss_feat_list,
.num_features = ARRAY_SIZE(am43xx_dss_feat_list),
.num_mgrs = 1,
.num_ovls = 3,
.supported_displays = am43xx_dss_supported_displays,
.supported_outputs = am43xx_dss_supported_outputs,
.supported_color_modes = omap3_dss_supported_color_modes,
.overlay_caps = omap3430_dss_overlay_caps,
.clksrc_names = omap2_dss_clk_source_names,
.dss_params = am43xx_dss_param_range,
.supported_rotation_types = OMAP_DSS_ROT_DMA,
.buffer_size_unit = 1,
.burst_size_unit = 8,
};
static const struct omap_dss_features omap3630_dss_features = {
.reg_fields = omap3_dss_reg_fields,
.num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields),
......@@ -777,7 +841,7 @@ static const struct omap_dss_features omap5_dss_features = {
.features = omap5_dss_feat_list,
.num_features = ARRAY_SIZE(omap5_dss_feat_list),
.num_mgrs = 3,
.num_mgrs = 4,
.num_ovls = 4,
.supported_displays = omap5_dss_supported_displays,
.supported_outputs = omap5_dss_supported_outputs,
......@@ -928,6 +992,10 @@ void dss_features_init(enum omapdss_version version)
omap_current_dss_features = &am35xx_dss_features;
break;
case OMAPDSS_VER_AM43xx:
omap_current_dss_features = &am43xx_dss_features;
break;
default:
DSSWARN("Unsupported OMAP version");
break;
......
......@@ -80,6 +80,7 @@
#define HDMI_TXPHY_DIGITAL_CTRL 0x4
#define HDMI_TXPHY_POWER_CTRL 0x8
#define HDMI_TXPHY_PAD_CFG_CTRL 0xC
#define HDMI_TXPHY_BIST_CONTROL 0x1C
enum hdmi_pll_pwr {
HDMI_PLLPWRCMD_ALLOFF = 0,
......@@ -351,7 +352,8 @@ struct hdmi_pll_data {
struct hdmi_phy_data {
void __iomem *base;
int irq;
u8 lane_function[4];
u8 lane_polarity[4];
};
struct hdmi_core_data {
......@@ -360,13 +362,13 @@ struct hdmi_core_data {
struct hdmi_core_infoframe_avi avi_cfg;
};
static inline void hdmi_write_reg(void __iomem *base_addr, const u16 idx,
static inline void hdmi_write_reg(void __iomem *base_addr, const u32 idx,
u32 val)
{
__raw_writel(val, base_addr + idx);
}
static inline u32 hdmi_read_reg(void __iomem *base_addr, const u16 idx)
static inline u32 hdmi_read_reg(void __iomem *base_addr, const u32 idx)
{
return __raw_readl(base_addr + idx);
}
......@@ -417,18 +419,19 @@ void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, int phy);
int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll);
/* HDMI PHY funcs */
int hdmi_phy_enable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp,
struct hdmi_config *cfg);
void hdmi_phy_disable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp);
int hdmi_phy_configure(struct hdmi_phy_data *phy, struct hdmi_config *cfg);
void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s);
int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy);
int hdmi_phy_parse_lanes(struct hdmi_phy_data *phy, const u32 *lanes);
/* HDMI common funcs */
const struct hdmi_config *hdmi_default_timing(void);
const struct hdmi_config *hdmi_get_timings(int mode, int code);
struct hdmi_cm hdmi_get_code(struct omap_video_timings *timing);
int hdmi_parse_lanes_of(struct platform_device *pdev, struct device_node *ep,
struct hdmi_phy_data *phy);
#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) || defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
int hdmi_compute_acr(u32 pclk, u32 sample_freq, u32 *n, u32 *cts);
int hdmi_wp_audio_enable(struct hdmi_wp_data *wp, bool enable);
int hdmi_wp_audio_core_req_enable(struct hdmi_wp_data *wp, bool enable);
......
......@@ -81,8 +81,40 @@ static void hdmi_runtime_put(void)
WARN_ON(r < 0 && r != -ENOSYS);
}
static irqreturn_t hdmi_irq_handler(int irq, void *data)
{
struct hdmi_wp_data *wp = data;
u32 irqstatus;
irqstatus = hdmi_wp_get_irqstatus(wp);
hdmi_wp_set_irqstatus(wp, irqstatus);
if ((irqstatus & HDMI_IRQ_LINK_CONNECT) &&
irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
/*
* If we get both connect and disconnect interrupts at the same
* time, turn off the PHY, clear interrupts, and restart, which
* raises connect interrupt if a cable is connected, or nothing
* if cable is not connected.
*/
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT |
HDMI_IRQ_LINK_DISCONNECT);
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
} else if (irqstatus & HDMI_IRQ_LINK_CONNECT) {
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON);
} else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
}
return IRQ_HANDLED;
}
static int hdmi_init_regulator(void)
{
int r;
struct regulator *reg;
if (hdmi.vdda_hdmi_dac_reg != NULL)
......@@ -96,6 +128,15 @@ static int hdmi_init_regulator(void)
return PTR_ERR(reg);
}
if (regulator_can_change_voltage(reg)) {
r = regulator_set_voltage(reg, 1800000, 1800000);
if (r) {
devm_regulator_put(reg);
DSSWARN("can't set the regulator voltage\n");
return r;
}
}
hdmi.vdda_hdmi_dac_reg = reg;
return 0;
......@@ -140,11 +181,16 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
struct omap_video_timings *p;
struct omap_overlay_manager *mgr = hdmi.output.manager;
unsigned long phy;
struct hdmi_wp_data *wp = &hdmi.wp;
r = hdmi_power_on_core(dssdev);
if (r)
return r;
/* disable and clear irqs */
hdmi_wp_clear_irqenable(wp, 0xffffffff);
hdmi_wp_set_irqstatus(wp, 0xffffffff);
p = &hdmi.cfg.timings;
DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res);
......@@ -161,12 +207,16 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
goto err_pll_enable;
}
r = hdmi_phy_enable(&hdmi.phy, &hdmi.wp, &hdmi.cfg);
r = hdmi_phy_configure(&hdmi.phy, &hdmi.cfg);
if (r) {
DSSDBG("Failed to start PHY\n");
goto err_phy_enable;
DSSDBG("Failed to configure PHY\n");
goto err_phy_cfg;
}
r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
if (r)
goto err_phy_pwr;
hdmi4_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg);
/* bypass TV gamma table */
......@@ -183,13 +233,17 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
if (r)
goto err_mgr_enable;
hdmi_wp_set_irqenable(wp,
HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
return 0;
err_mgr_enable:
hdmi_wp_video_stop(&hdmi.wp);
err_vid_enable:
hdmi_phy_disable(&hdmi.phy, &hdmi.wp);
err_phy_enable:
err_phy_cfg:
hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
err_phy_pwr:
hdmi_pll_disable(&hdmi.pll, &hdmi.wp);
err_pll_enable:
hdmi_power_off_core(dssdev);
......@@ -200,10 +254,14 @@ static void hdmi_power_off_full(struct omap_dss_device *dssdev)
{
struct omap_overlay_manager *mgr = hdmi.output.manager;
hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff);
dss_mgr_disable(mgr);
hdmi_wp_video_stop(&hdmi.wp);
hdmi_phy_disable(&hdmi.phy, &hdmi.wp);
hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
hdmi_pll_disable(&hdmi.pll, &hdmi.wp);
hdmi_power_off_core(dssdev);
......@@ -600,15 +658,44 @@ static void __exit hdmi_uninit_output(struct platform_device *pdev)
omapdss_unregister_output(out);
}
static int hdmi_probe_of(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct device_node *ep;
int r;
ep = omapdss_of_get_first_endpoint(node);
if (!ep)
return 0;
r = hdmi_parse_lanes_of(pdev, ep, &hdmi.phy);
if (r)
goto err;
of_node_put(ep);
return 0;
err:
of_node_put(ep);
return r;
}
/* HDMI HW IP initialisation */
static int omapdss_hdmihw_probe(struct platform_device *pdev)
{
int r;
int irq;
hdmi.pdev = pdev;
mutex_init(&hdmi.lock);
if (pdev->dev.of_node) {
r = hdmi_probe_of(pdev);
if (r)
return r;
}
r = hdmi_wp_init(pdev, &hdmi.wp);
if (r)
return r;
......@@ -631,6 +718,20 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
return r;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
DSSERR("platform_get_irq failed\n");
return -ENODEV;
}
r = devm_request_threaded_irq(&pdev->dev, irq,
NULL, hdmi_irq_handler,
IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp);
if (r) {
DSSERR("HDMI IRQ request failed\n");
return r;
}
pm_runtime_enable(&pdev->dev);
hdmi_init_output(pdev);
......
......@@ -998,38 +998,20 @@ int hdmi4_audio_get_dma_port(u32 *offset, u32 *size)
#endif
#define CORE_OFFSET 0x400
#define CORE_SIZE 0xc00
int hdmi4_core_init(struct platform_device *pdev, struct hdmi_core_data *core)
{
struct resource *res;
struct resource temp_res;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
if (!res) {
DSSDBG("can't get CORE mem resource by name\n");
/*
* if hwmod/DT doesn't have the memory resource information
* split into HDMI sub blocks by name, we try again by getting
* the platform's first resource. this code will be removed when
* the driver can get the mem resources by name
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
DSSERR("can't get CORE mem resource\n");
return -EINVAL;
}
temp_res.start = res->start + CORE_OFFSET;
temp_res.end = temp_res.start + CORE_SIZE - 1;
res = &temp_res;
DSSERR("can't get CORE mem resource\n");
return -EINVAL;
}
core->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!core->base) {
core->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(core->base)) {
DSSERR("can't ioremap CORE\n");
return -ENOMEM;
return PTR_ERR(core->base);
}
return 0;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -17,6 +17,7 @@
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/of.h>
#include <video/omapdss.h>
#include "hdmi.h"
......@@ -323,6 +324,46 @@ struct hdmi_cm hdmi_get_code(struct omap_video_timings *timing)
return cm;
}
int hdmi_parse_lanes_of(struct platform_device *pdev, struct device_node *ep,
struct hdmi_phy_data *phy)
{
struct property *prop;
int r, len;
prop = of_find_property(ep, "lanes", &len);
if (prop) {
u32 lanes[8];
if (len / sizeof(u32) != ARRAY_SIZE(lanes)) {
dev_err(&pdev->dev, "bad number of lanes\n");
return -EINVAL;
}
r = of_property_read_u32_array(ep, "lanes", lanes,
ARRAY_SIZE(lanes));
if (r) {
dev_err(&pdev->dev, "failed to read lane data\n");
return r;
}
r = hdmi_phy_parse_lanes(phy, lanes);
if (r) {
dev_err(&pdev->dev, "failed to parse lane data\n");
return r;
}
} else {
static const u32 default_lanes[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
r = hdmi_phy_parse_lanes(phy, default_lanes);
if (WARN_ON(r)) {
dev_err(&pdev->dev, "failed to parse lane data\n");
return r;
}
}
return 0;
}
#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
int hdmi_compute_acr(u32 pclk, u32 sample_freq, u32 *n, u32 *cts)
{
......
......@@ -12,11 +12,22 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <video/omapdss.h>
#include "dss.h"
#include "hdmi.h"
struct hdmi_phy_features {
bool bist_ctrl;
bool calc_freqout;
bool ldo_voltage;
unsigned long dcofreq_min;
unsigned long max_phy;
};
static const struct hdmi_phy_features *phy_feat;
void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s)
{
#define DUMPPHY(r) seq_printf(s, "%-35s %08x\n", #r,\
......@@ -26,53 +37,104 @@ void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s)
DUMPPHY(HDMI_TXPHY_DIGITAL_CTRL);
DUMPPHY(HDMI_TXPHY_POWER_CTRL);
DUMPPHY(HDMI_TXPHY_PAD_CFG_CTRL);
if (phy_feat->bist_ctrl)
DUMPPHY(HDMI_TXPHY_BIST_CONTROL);
}
static irqreturn_t hdmi_irq_handler(int irq, void *data)
int hdmi_phy_parse_lanes(struct hdmi_phy_data *phy, const u32 *lanes)
{
struct hdmi_wp_data *wp = data;
u32 irqstatus;
irqstatus = hdmi_wp_get_irqstatus(wp);
hdmi_wp_set_irqstatus(wp, irqstatus);
if ((irqstatus & HDMI_IRQ_LINK_CONNECT) &&
irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
/*
* If we get both connect and disconnect interrupts at the same
* time, turn off the PHY, clear interrupts, and restart, which
* raises connect interrupt if a cable is connected, or nothing
* if cable is not connected.
*/
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT |
HDMI_IRQ_LINK_DISCONNECT);
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
} else if (irqstatus & HDMI_IRQ_LINK_CONNECT) {
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON);
} else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
int i;
for (i = 0; i < 8; i += 2) {
u8 lane, pol;
int dx, dy;
dx = lanes[i];
dy = lanes[i + 1];
if (dx < 0 || dx >= 8)
return -EINVAL;
if (dy < 0 || dy >= 8)
return -EINVAL;
if (dx & 1) {
if (dy != dx - 1)
return -EINVAL;
pol = 1;
} else {
if (dy != dx + 1)
return -EINVAL;
pol = 0;
}
lane = dx / 2;
phy->lane_function[lane] = i / 2;
phy->lane_polarity[lane] = pol;
}
return IRQ_HANDLED;
return 0;
}
int hdmi_phy_enable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp,
struct hdmi_config *cfg)
static void hdmi_phy_configure_lanes(struct hdmi_phy_data *phy)
{
u16 r = 0;
u32 irqstatus;
hdmi_wp_clear_irqenable(wp, 0xffffffff);
irqstatus = hdmi_wp_get_irqstatus(wp);
hdmi_wp_set_irqstatus(wp, irqstatus);
static const u16 pad_cfg_list[] = {
0x0123,
0x0132,
0x0312,
0x0321,
0x0231,
0x0213,
0x1023,
0x1032,
0x3012,
0x3021,
0x2031,
0x2013,
0x1203,
0x1302,
0x3102,
0x3201,
0x2301,
0x2103,
0x1230,
0x1320,
0x3120,
0x3210,
0x2310,
0x2130,
};
u16 lane_cfg = 0;
int i;
unsigned lane_cfg_val;
u16 pol_val = 0;
for (i = 0; i < 4; ++i)
lane_cfg |= phy->lane_function[i] << ((3 - i) * 4);
pol_val |= phy->lane_polarity[0] << 0;
pol_val |= phy->lane_polarity[1] << 3;
pol_val |= phy->lane_polarity[2] << 2;
pol_val |= phy->lane_polarity[3] << 1;
for (i = 0; i < ARRAY_SIZE(pad_cfg_list); ++i)
if (pad_cfg_list[i] == lane_cfg)
break;
if (WARN_ON(i == ARRAY_SIZE(pad_cfg_list)))
i = 0;
lane_cfg_val = i;
REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, lane_cfg_val, 26, 22);
REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, pol_val, 30, 27);
}
r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
if (r)
return r;
int hdmi_phy_configure(struct hdmi_phy_data *phy, struct hdmi_config *cfg)
{
u8 freqout;
/*
* Read address 0 in order to get the SCP reset done completed
......@@ -80,80 +142,113 @@ int hdmi_phy_enable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp,
*/
hdmi_read_reg(phy->base, HDMI_TXPHY_TX_CTRL);
/*
* In OMAP5+, the HFBITCLK must be divided by 2 before issuing the
* HDMI_PHYPWRCMD_LDOON command.
*/
if (phy_feat->bist_ctrl)
REG_FLD_MOD(phy->base, HDMI_TXPHY_BIST_CONTROL, 1, 11, 11);
if (phy_feat->calc_freqout) {
/* DCOCLK/10 is pixel clock, compare pclk with DCOCLK_MIN/10 */
u32 dco_min = phy_feat->dcofreq_min / 10;
u32 pclk = cfg->timings.pixelclock;
if (pclk < dco_min)
freqout = 0;
else if ((pclk >= dco_min) && (pclk < phy_feat->max_phy))
freqout = 1;
else
freqout = 2;
} else {
freqout = 1;
}
/*
* Write to phy address 0 to configure the clock
* use HFBITCLK write HDMI_TXPHY_TX_CONTROL_FREQOUT field
*/
REG_FLD_MOD(phy->base, HDMI_TXPHY_TX_CTRL, 0x1, 31, 30);
REG_FLD_MOD(phy->base, HDMI_TXPHY_TX_CTRL, freqout, 31, 30);
/* Write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */
hdmi_write_reg(phy->base, HDMI_TXPHY_DIGITAL_CTRL, 0xF0000000);
/* Setup max LDO voltage */
REG_FLD_MOD(phy->base, HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0);
if (phy_feat->ldo_voltage)
REG_FLD_MOD(phy->base, HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0);
/* Write to phy address 3 to change the polarity control */
REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27);
r = request_threaded_irq(phy->irq, NULL, hdmi_irq_handler,
IRQF_ONESHOT, "OMAP HDMI", wp);
if (r) {
DSSERR("HDMI IRQ request failed\n");
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
return r;
}
hdmi_wp_set_irqenable(wp,
HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
hdmi_phy_configure_lanes(phy);
return 0;
}
void hdmi_phy_disable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp)
static const struct hdmi_phy_features omap44xx_phy_feats = {
.bist_ctrl = false,
.calc_freqout = false,
.ldo_voltage = true,
.dcofreq_min = 500000000,
.max_phy = 185675000,
};
static const struct hdmi_phy_features omap54xx_phy_feats = {
.bist_ctrl = true,
.calc_freqout = true,
.ldo_voltage = false,
.dcofreq_min = 750000000,
.max_phy = 186000000,
};
static int hdmi_phy_init_features(struct platform_device *pdev)
{
free_irq(phy->irq, wp);
struct hdmi_phy_features *dst;
const struct hdmi_phy_features *src;
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
}
dst = devm_kzalloc(&pdev->dev, sizeof(*dst), GFP_KERNEL);
if (!dst) {
dev_err(&pdev->dev, "Failed to allocate HDMI PHY Features\n");
return -ENOMEM;
}
switch (omapdss_get_version()) {
case OMAPDSS_VER_OMAP4430_ES1:
case OMAPDSS_VER_OMAP4430_ES2:
case OMAPDSS_VER_OMAP4:
src = &omap44xx_phy_feats;
break;
#define PHY_OFFSET 0x300
#define PHY_SIZE 0x100
case OMAPDSS_VER_OMAP5:
src = &omap54xx_phy_feats;
break;
default:
return -ENODEV;
}
memcpy(dst, src, sizeof(*dst));
phy_feat = dst;
return 0;
}
int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy)
{
int r;
struct resource *res;
struct resource temp_res;
r = hdmi_phy_init_features(pdev);
if (r)
return r;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
if (!res) {
DSSDBG("can't get PHY mem resource by name\n");
/*
* if hwmod/DT doesn't have the memory resource information
* split into HDMI sub blocks by name, we try again by getting
* the platform's first resource. this code will be removed when
* the driver can get the mem resources by name
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
DSSERR("can't get PHY mem resource\n");
return -EINVAL;
}
temp_res.start = res->start + PHY_OFFSET;
temp_res.end = temp_res.start + PHY_SIZE - 1;
res = &temp_res;
DSSERR("can't get PHY mem resource\n");
return -EINVAL;
}
phy->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!phy->base) {
phy->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(phy->base)) {
DSSERR("can't ioremap TX PHY\n");
return -ENOMEM;
}
phy->irq = platform_get_irq(pdev, 0);
if (phy->irq < 0) {
DSSERR("platform_get_irq failed\n");
return -ENODEV;
return PTR_ERR(phy->base);
}
return 0;
......
......@@ -23,6 +23,18 @@
#define HDMI_DEFAULT_REGN 16
#define HDMI_DEFAULT_REGM2 1
struct hdmi_pll_features {
bool sys_reset;
/* this is a hack, need to replace it with a better computation of M2 */
bool bound_dcofreq;
unsigned long fint_min, fint_max;
u16 regm_max;
unsigned long dcofreq_low_min, dcofreq_low_max;
unsigned long dcofreq_high_min, dcofreq_high_max;
};
static const struct hdmi_pll_features *pll_feat;
void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s)
{
#define DUMPPLL(r) seq_printf(s, "%-35s %08x\n", #r,\
......@@ -57,7 +69,11 @@ void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, int phy)
refclk = clkin / pi->regn;
pi->regm2 = HDMI_DEFAULT_REGM2;
/* temorary hack to make sure DCO freq isn't calculated too low */
if (pll_feat->bound_dcofreq && phy <= 65000)
pi->regm2 = 3;
else
pi->regm2 = HDMI_DEFAULT_REGM2;
/*
* multiplier is pixel_clk/ref_clk
......@@ -154,7 +170,7 @@ static int hdmi_pll_config(struct hdmi_pll_data *pll)
static int hdmi_pll_reset(struct hdmi_pll_data *pll)
{
/* SYSRESET controlled by power FSM */
REG_FLD_MOD(pll->base, PLLCTRL_PLL_CONTROL, 0x0, 3, 3);
REG_FLD_MOD(pll->base, PLLCTRL_PLL_CONTROL, pll_feat->sys_reset, 3, 3);
/* READ 0x0 reset is in progress */
if (hdmi_wait_for_bit_change(pll->base, PLLCTRL_PLL_STATUS, 0, 0, 1)
......@@ -194,38 +210,81 @@ void hdmi_pll_disable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp)
hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF);
}
#define PLL_OFFSET 0x200
#define PLL_SIZE 0x100
static const struct hdmi_pll_features omap44xx_pll_feats = {
.sys_reset = false,
.bound_dcofreq = false,
.fint_min = 500000,
.fint_max = 2500000,
.regm_max = 4095,
.dcofreq_low_min = 500000000,
.dcofreq_low_max = 1000000000,
.dcofreq_high_min = 1000000000,
.dcofreq_high_max = 2000000000,
};
static const struct hdmi_pll_features omap54xx_pll_feats = {
.sys_reset = true,
.bound_dcofreq = true,
.fint_min = 620000,
.fint_max = 2500000,
.regm_max = 2046,
.dcofreq_low_min = 750000000,
.dcofreq_low_max = 1500000000,
.dcofreq_high_min = 1250000000,
.dcofreq_high_max = 2500000000UL,
};
static int hdmi_pll_init_features(struct platform_device *pdev)
{
struct hdmi_pll_features *dst;
const struct hdmi_pll_features *src;
dst = devm_kzalloc(&pdev->dev, sizeof(*dst), GFP_KERNEL);
if (!dst) {
dev_err(&pdev->dev, "Failed to allocate HDMI PHY Features\n");
return -ENOMEM;
}
switch (omapdss_get_version()) {
case OMAPDSS_VER_OMAP4430_ES1:
case OMAPDSS_VER_OMAP4430_ES2:
case OMAPDSS_VER_OMAP4:
src = &omap44xx_pll_feats;
break;
case OMAPDSS_VER_OMAP5:
src = &omap54xx_pll_feats;
break;
default:
return -ENODEV;
}
memcpy(dst, src, sizeof(*dst));
pll_feat = dst;
return 0;
}
int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll)
{
int r;
struct resource *res;
struct resource temp_res;
r = hdmi_pll_init_features(pdev);
if (r)
return r;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll");
if (!res) {
DSSDBG("can't get PLL mem resource by name\n");
/*
* if hwmod/DT doesn't have the memory resource information
* split into HDMI sub blocks by name, we try again by getting
* the platform's first resource. this code will be removed when
* the driver can get the mem resources by name
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
DSSERR("can't get PLL mem resource\n");
return -EINVAL;
}
temp_res.start = res->start + PLL_OFFSET;
temp_res.end = temp_res.start + PLL_SIZE - 1;
res = &temp_res;
DSSERR("can't get PLL mem resource\n");
return -EINVAL;
}
pll->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!pll->base) {
pll->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(pll->base)) {
DSSERR("can't ioremap PLLCTRL\n");
return -ENOMEM;
return PTR_ERR(pll->base);
}
return 0;
......
......@@ -185,7 +185,7 @@ void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt,
timings->interlace = param->timings.interlace;
}
#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) || defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
void hdmi_wp_audio_config_format(struct hdmi_wp_data *wp,
struct hdmi_audio_format *aud_fmt)
{
......@@ -238,37 +238,20 @@ int hdmi_wp_audio_core_req_enable(struct hdmi_wp_data *wp, bool enable)
}
#endif
#define WP_SIZE 0x200
int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp)
{
struct resource *res;
struct resource temp_res;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wp");
if (!res) {
DSSDBG("can't get WP mem resource by name\n");
/*
* if hwmod/DT doesn't have the memory resource information
* split into HDMI sub blocks by name, we try again by getting
* the platform's first resource. this code will be removed when
* the driver can get the mem resources by name
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
DSSERR("can't get WP mem resource\n");
return -EINVAL;
}
temp_res.start = res->start;
temp_res.end = temp_res.start + WP_SIZE - 1;
res = &temp_res;
DSSERR("can't get WP mem resource\n");
return -EINVAL;
}
wp->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!wp->base) {
wp->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(wp->base)) {
DSSERR("can't ioremap HDMI WP\n");
return -ENOMEM;
return PTR_ERR(wp->base);
}
return 0;
......
/*
* Copyright (C) 2014 Texas Instruments
* Author: Tomi Valkeinen <tomi.valkeinen@ti.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/>.
*/
/*
* As omapdss panel drivers are omapdss specific, but we want to define the
* DT-data in generic manner, we convert the compatible strings of the panel and
* encoder nodes from "panel-foo" to "omapdss,panel-foo". This way we can have
* both correct DT data and omapdss specific drivers.
*
* When we get generic panel drivers to the kernel, this file will be removed.
*/
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/slab.h>
#include <linux/list.h>
static struct list_head dss_conv_list __initdata;
static const char prefix[] __initconst = "omapdss,";
struct dss_conv_node {
struct list_head list;
struct device_node *node;
bool root;
};
static int __init omapdss_count_strings(const struct property *prop)
{
const char *p = prop->value;
int l = 0, total = 0;
int i;
for (i = 0; total < prop->length; total += l, p += l, i++)
l = strlen(p) + 1;
return i;
}
static void __init omapdss_update_prop(struct device_node *node, char *compat,
int len)
{
struct property *prop;
prop = kzalloc(sizeof(*prop), GFP_KERNEL);
if (!prop)
return;
prop->name = "compatible";
prop->value = compat;
prop->length = len;
of_update_property(node, prop);
}
static void __init omapdss_prefix_strcpy(char *dst, int dst_len,
const char *src, int src_len)
{
size_t total = 0;
while (total < src_len) {
size_t l = strlen(src) + 1;
strcpy(dst, prefix);
dst += strlen(prefix);
strcpy(dst, src);
dst += l;
src += l;
total += l;
}
}
/* prepend compatible property strings with "omapdss," */
static void __init omapdss_omapify_node(struct device_node *node)
{
struct property *prop;
char *new_compat;
int num_strs;
int new_len;
prop = of_find_property(node, "compatible", NULL);
if (!prop || !prop->value)
return;
if (strnlen(prop->value, prop->length) >= prop->length)
return;
/* is it already prefixed? */
if (strncmp(prefix, prop->value, strlen(prefix)) == 0)
return;
num_strs = omapdss_count_strings(prop);
new_len = prop->length + strlen(prefix) * num_strs;
new_compat = kmalloc(new_len, GFP_KERNEL);
omapdss_prefix_strcpy(new_compat, new_len, prop->value, prop->length);
omapdss_update_prop(node, new_compat, new_len);
}
static void __init omapdss_add_to_list(struct device_node *node, bool root)
{
struct dss_conv_node *n = kmalloc(sizeof(struct dss_conv_node),
GFP_KERNEL);
n->node = node;
n->root = root;
list_add(&n->list, &dss_conv_list);
}
static bool __init omapdss_list_contains(const struct device_node *node)
{
struct dss_conv_node *n;
list_for_each_entry(n, &dss_conv_list, list) {
if (n->node == node)
return true;
}
return false;
}
static void __init omapdss_walk_device(struct device_node *node, bool root)
{
struct device_node *n;
omapdss_add_to_list(node, root);
/*
* of_graph_get_remote_port_parent() prints an error if there is no
* port/ports node. To avoid that, check first that there's the node.
*/
n = of_get_child_by_name(node, "ports");
if (!n)
n = of_get_child_by_name(node, "port");
if (!n)
return;
of_node_put(n);
n = NULL;
while ((n = of_graph_get_next_endpoint(node, n)) != NULL) {
struct device_node *pn;
pn = of_graph_get_remote_port_parent(n);
if (!pn) {
of_node_put(n);
continue;
}
if (!of_device_is_available(pn) || omapdss_list_contains(pn)) {
of_node_put(pn);
of_node_put(n);
continue;
}
omapdss_walk_device(pn, false);
of_node_put(n);
}
}
static const struct of_device_id omapdss_of_match[] __initconst = {
{ .compatible = "ti,omap2-dss", },
{ .compatible = "ti,omap3-dss", },
{ .compatible = "ti,omap4-dss", },
{ .compatible = "ti,omap5-dss", },
{},
};
static int __init omapdss_boot_init(void)
{
struct device_node *dss, *child;
INIT_LIST_HEAD(&dss_conv_list);
dss = of_find_matching_node(NULL, omapdss_of_match);
if (dss == NULL || !of_device_is_available(dss))
return 0;
omapdss_walk_device(dss, true);
for_each_available_child_of_node(dss, child) {
if (!of_find_property(child, "compatible", NULL)) {
of_node_put(child);
continue;
}
omapdss_walk_device(child, true);
}
while (!list_empty(&dss_conv_list)) {
struct dss_conv_node *n;
n = list_first_entry(&dss_conv_list, struct dss_conv_node,
list);
if (!n->root)
omapdss_omapify_node(n->node);
list_del(&n->list);
of_node_put(n->node);
kfree(n);
}
return 0;
}
subsys_initcall(omapdss_boot_init);
This diff is collapsed.
......@@ -319,6 +319,7 @@ enum omapdss_version {
OMAPDSS_VER_OMAP4430_ES2, /* OMAP4430 ES2.0, 2.1, 2.2 */
OMAPDSS_VER_OMAP4, /* All other OMAP4s */
OMAPDSS_VER_OMAP5,
OMAPDSS_VER_AM43xx,
};
/* Board specific data */
......@@ -388,8 +389,8 @@ struct omap_dss_cpr_coefs {
};
struct omap_overlay_info {
u32 paddr;
u32 p_uv_addr; /* for NV12 format */
dma_addr_t paddr;
dma_addr_t p_uv_addr; /* for NV12 format */
u16 screen_width;
u16 width;
u16 height;
......@@ -964,9 +965,6 @@ int dispc_ovl_setup(enum omap_plane plane, const struct omap_overlay_info *oi,
bool replication, const struct omap_video_timings *mgr_timings,
bool mem_to_mem);
#define to_dss_driver(x) container_of((x), struct omap_dss_driver, driver)
#define to_dss_device(x) container_of((x), struct omap_dss_device, old_dev)
int omapdss_compat_init(void);
void omapdss_compat_uninit(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment