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;
......
/*
* HDMI driver for OMAP5
*
* Copyright (C) 2014 Texas Instruments Incorporated
*
* Authors:
* Yong Zhi
* Mythri pk
* Archit Taneja <archit@ti.com>
* 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/>.
*/
#define DSS_SUBSYS_NAME "HDMI"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <video/omapdss.h>
#include "hdmi5_core.h"
#include "dss.h"
#include "dss_features.h"
static struct {
struct mutex lock;
struct platform_device *pdev;
struct hdmi_wp_data wp;
struct hdmi_pll_data pll;
struct hdmi_phy_data phy;
struct hdmi_core_data core;
struct hdmi_config cfg;
struct clk *sys_clk;
struct regulator *vdda_reg;
bool core_enabled;
struct omap_dss_device output;
} hdmi;
static int hdmi_runtime_get(void)
{
int r;
DSSDBG("hdmi_runtime_get\n");
r = pm_runtime_get_sync(&hdmi.pdev->dev);
WARN_ON(r < 0);
if (r < 0)
return r;
return 0;
}
static void hdmi_runtime_put(void)
{
int r;
DSSDBG("hdmi_runtime_put\n");
r = pm_runtime_put_sync(&hdmi.pdev->dev);
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) {
u32 v;
/*
* 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);
/*
* We always get bogus CONNECT & DISCONNECT interrupts when
* setting the PHY to LDOON. To ignore those, we force the RXDET
* line to 0 until the PHY power state has been changed.
*/
v = hdmi_read_reg(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL);
v = FLD_MOD(v, 1, 15, 15); /* FORCE_RXDET_HIGH */
v = FLD_MOD(v, 0, 14, 7); /* RXDET_LINE */
hdmi_write_reg(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL, v);
hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT |
HDMI_IRQ_LINK_DISCONNECT);
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
REG_FLD_MOD(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL, 0, 15, 15);
} 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_reg != NULL)
return 0;
reg = devm_regulator_get(&hdmi.pdev->dev, "vdda");
if (IS_ERR(reg)) {
DSSERR("can't get VDDA regulator\n");
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_reg = reg;
return 0;
}
static int hdmi_power_on_core(struct omap_dss_device *dssdev)
{
int r;
r = regulator_enable(hdmi.vdda_reg);
if (r)
return r;
r = hdmi_runtime_get();
if (r)
goto err_runtime_get;
/* Make selection of HDMI in DSS */
dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK);
hdmi.core_enabled = true;
return 0;
err_runtime_get:
regulator_disable(hdmi.vdda_reg);
return r;
}
static void hdmi_power_off_core(struct omap_dss_device *dssdev)
{
hdmi.core_enabled = false;
hdmi_runtime_put();
regulator_disable(hdmi.vdda_reg);
}
static int hdmi_power_on_full(struct omap_dss_device *dssdev)
{
int r;
struct omap_video_timings *p;
struct omap_overlay_manager *mgr = hdmi.output.manager;
unsigned long phy;
r = hdmi_power_on_core(dssdev);
if (r)
return r;
p = &hdmi.cfg.timings;
DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res);
/* the functions below use kHz pixel clock. TODO: change to Hz */
phy = p->pixelclock / 1000;
hdmi_pll_compute(&hdmi.pll, clk_get_rate(hdmi.sys_clk), phy);
/* disable and clear irqs */
hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff);
hdmi_wp_set_irqstatus(&hdmi.wp,
hdmi_wp_get_irqstatus(&hdmi.wp));
/* config the PLL and PHY hdmi_set_pll_pwrfirst */
r = hdmi_pll_enable(&hdmi.pll, &hdmi.wp);
if (r) {
DSSDBG("Failed to lock PLL\n");
goto err_pll_enable;
}
r = hdmi_phy_configure(&hdmi.phy, &hdmi.cfg);
if (r) {
DSSDBG("Failed to start PHY\n");
goto err_phy_cfg;
}
r = hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_LDOON);
if (r)
goto err_phy_pwr;
hdmi5_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg);
/* bypass TV gamma table */
dispc_enable_gamma_table(0);
/* tv size */
dss_mgr_set_timings(mgr, p);
r = hdmi_wp_video_start(&hdmi.wp);
if (r)
goto err_vid_enable;
r = dss_mgr_enable(mgr);
if (r)
goto err_mgr_enable;
hdmi_wp_set_irqenable(&hdmi.wp,
HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
return 0;
err_mgr_enable:
hdmi_wp_video_stop(&hdmi.wp);
err_vid_enable:
hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
err_phy_pwr:
err_phy_cfg:
hdmi_pll_disable(&hdmi.pll, &hdmi.wp);
err_pll_enable:
hdmi_power_off_core(dssdev);
return -EIO;
}
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_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
hdmi_pll_disable(&hdmi.pll, &hdmi.wp);
hdmi_power_off_core(dssdev);
}
static int hdmi_display_check_timing(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct omap_dss_device *out = &hdmi.output;
if (!dispc_mgr_timings_ok(out->dispc_channel, timings))
return -EINVAL;
return 0;
}
static void hdmi_display_set_timing(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct hdmi_cm cm;
const struct hdmi_config *t;
mutex_lock(&hdmi.lock);
cm = hdmi_get_code(timings);
hdmi.cfg.cm = cm;
t = hdmi_get_timings(cm.mode, cm.code);
if (t != NULL) {
hdmi.cfg = *t;
dispc_set_tv_pclk(t->timings.pixelclock);
} else {
hdmi.cfg.timings = *timings;
hdmi.cfg.cm.code = 0;
hdmi.cfg.cm.mode = HDMI_DVI;
dispc_set_tv_pclk(timings->pixelclock);
}
DSSDBG("using mode: %s, code %d\n", hdmi.cfg.cm.mode == HDMI_DVI ?
"DVI" : "HDMI", hdmi.cfg.cm.code);
mutex_unlock(&hdmi.lock);
}
static void hdmi_display_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
const struct hdmi_config *cfg;
struct hdmi_cm cm = hdmi.cfg.cm;
cfg = hdmi_get_timings(cm.mode, cm.code);
if (cfg == NULL)
cfg = hdmi_default_timing();
memcpy(timings, &cfg->timings, sizeof(cfg->timings));
}
static void hdmi_dump_regs(struct seq_file *s)
{
mutex_lock(&hdmi.lock);
if (hdmi_runtime_get()) {
mutex_unlock(&hdmi.lock);
return;
}
hdmi_wp_dump(&hdmi.wp, s);
hdmi_pll_dump(&hdmi.pll, s);
hdmi_phy_dump(&hdmi.phy, s);
hdmi5_core_dump(&hdmi.core, s);
hdmi_runtime_put();
mutex_unlock(&hdmi.lock);
}
static int read_edid(u8 *buf, int len)
{
int r;
int idlemode;
mutex_lock(&hdmi.lock);
r = hdmi_runtime_get();
BUG_ON(r);
idlemode = REG_GET(hdmi.wp.base, HDMI_WP_SYSCONFIG, 3, 2);
/* No-idle mode */
REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2);
r = hdmi5_read_edid(&hdmi.core, buf, len);
REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, idlemode, 3, 2);
hdmi_runtime_put();
mutex_unlock(&hdmi.lock);
return r;
}
static int hdmi_display_enable(struct omap_dss_device *dssdev)
{
struct omap_dss_device *out = &hdmi.output;
int r = 0;
DSSDBG("ENTER hdmi_display_enable\n");
mutex_lock(&hdmi.lock);
if (out == NULL || out->manager == NULL) {
DSSERR("failed to enable display: no output/manager\n");
r = -ENODEV;
goto err0;
}
r = hdmi_power_on_full(dssdev);
if (r) {
DSSERR("failed to power on device\n");
goto err0;
}
mutex_unlock(&hdmi.lock);
return 0;
err0:
mutex_unlock(&hdmi.lock);
return r;
}
static void hdmi_display_disable(struct omap_dss_device *dssdev)
{
DSSDBG("Enter hdmi_display_disable\n");
mutex_lock(&hdmi.lock);
hdmi_power_off_full(dssdev);
mutex_unlock(&hdmi.lock);
}
static int hdmi_core_enable(struct omap_dss_device *dssdev)
{
int r = 0;
DSSDBG("ENTER omapdss_hdmi_core_enable\n");
mutex_lock(&hdmi.lock);
r = hdmi_power_on_core(dssdev);
if (r) {
DSSERR("failed to power on device\n");
goto err0;
}
mutex_unlock(&hdmi.lock);
return 0;
err0:
mutex_unlock(&hdmi.lock);
return r;
}
static void hdmi_core_disable(struct omap_dss_device *dssdev)
{
DSSDBG("Enter omapdss_hdmi_core_disable\n");
mutex_lock(&hdmi.lock);
hdmi_power_off_core(dssdev);
mutex_unlock(&hdmi.lock);
}
static int hdmi_get_clocks(struct platform_device *pdev)
{
struct clk *clk;
clk = devm_clk_get(&pdev->dev, "sys_clk");
if (IS_ERR(clk)) {
DSSERR("can't get sys_clk\n");
return PTR_ERR(clk);
}
hdmi.sys_clk = clk;
return 0;
}
static int hdmi_connect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
struct omap_overlay_manager *mgr;
int r;
r = hdmi_init_regulator();
if (r)
return r;
mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel);
if (!mgr)
return -ENODEV;
r = dss_mgr_connect(mgr, dssdev);
if (r)
return r;
r = omapdss_output_set_device(dssdev, dst);
if (r) {
DSSERR("failed to connect output to new device: %s\n",
dst->name);
dss_mgr_disconnect(mgr, dssdev);
return r;
}
return 0;
}
static void hdmi_disconnect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
WARN_ON(dst != dssdev->dst);
if (dst != dssdev->dst)
return;
omapdss_output_unset_device(dssdev);
if (dssdev->manager)
dss_mgr_disconnect(dssdev->manager, dssdev);
}
static int hdmi_read_edid(struct omap_dss_device *dssdev,
u8 *edid, int len)
{
bool need_enable;
int r;
need_enable = hdmi.core_enabled == false;
if (need_enable) {
r = hdmi_core_enable(dssdev);
if (r)
return r;
}
r = read_edid(edid, len);
if (need_enable)
hdmi_core_disable(dssdev);
return r;
}
#if defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
static int hdmi_audio_enable(struct omap_dss_device *dssdev)
{
int r;
mutex_lock(&hdmi.lock);
if (!hdmi_mode_has_audio(hdmi.cfg.cm.mode)) {
r = -EPERM;
goto err;
}
r = hdmi_wp_audio_enable(&hdmi.wp, true);
if (r)
goto err;
mutex_unlock(&hdmi.lock);
return 0;
err:
mutex_unlock(&hdmi.lock);
return r;
}
static void hdmi_audio_disable(struct omap_dss_device *dssdev)
{
hdmi_wp_audio_enable(&hdmi.wp, false);
}
static int hdmi_audio_start(struct omap_dss_device *dssdev)
{
return hdmi_wp_audio_core_req_enable(&hdmi.wp, true);
}
static void hdmi_audio_stop(struct omap_dss_device *dssdev)
{
hdmi_wp_audio_core_req_enable(&hdmi.wp, false);
}
static bool hdmi_audio_supported(struct omap_dss_device *dssdev)
{
bool r;
mutex_lock(&hdmi.lock);
r = hdmi_mode_has_audio(hdmi.cfg.cm.mode);
mutex_unlock(&hdmi.lock);
return r;
}
static int hdmi_audio_config(struct omap_dss_device *dssdev,
struct omap_dss_audio *audio)
{
int r;
u32 pclk = hdmi.cfg.timings.pixelclock;
mutex_lock(&hdmi.lock);
if (!hdmi_mode_has_audio(hdmi.cfg.cm.mode)) {
r = -EPERM;
goto err;
}
r = hdmi5_audio_config(&hdmi.core, &hdmi.wp, audio, pclk);
if (r)
goto err;
mutex_unlock(&hdmi.lock);
return 0;
err:
mutex_unlock(&hdmi.lock);
return r;
}
#else
static int hdmi_audio_enable(struct omap_dss_device *dssdev)
{
return -EPERM;
}
static void hdmi_audio_disable(struct omap_dss_device *dssdev)
{
}
static int hdmi_audio_start(struct omap_dss_device *dssdev)
{
return -EPERM;
}
static void hdmi_audio_stop(struct omap_dss_device *dssdev)
{
}
static bool hdmi_audio_supported(struct omap_dss_device *dssdev)
{
return false;
}
static int hdmi_audio_config(struct omap_dss_device *dssdev,
struct omap_dss_audio *audio)
{
return -EPERM;
}
#endif
static const struct omapdss_hdmi_ops hdmi_ops = {
.connect = hdmi_connect,
.disconnect = hdmi_disconnect,
.enable = hdmi_display_enable,
.disable = hdmi_display_disable,
.check_timings = hdmi_display_check_timing,
.set_timings = hdmi_display_set_timing,
.get_timings = hdmi_display_get_timings,
.read_edid = hdmi_read_edid,
.audio_enable = hdmi_audio_enable,
.audio_disable = hdmi_audio_disable,
.audio_start = hdmi_audio_start,
.audio_stop = hdmi_audio_stop,
.audio_supported = hdmi_audio_supported,
.audio_config = hdmi_audio_config,
};
static void hdmi_init_output(struct platform_device *pdev)
{
struct omap_dss_device *out = &hdmi.output;
out->dev = &pdev->dev;
out->id = OMAP_DSS_OUTPUT_HDMI;
out->output_type = OMAP_DISPLAY_TYPE_HDMI;
out->name = "hdmi.0";
out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
out->ops.hdmi = &hdmi_ops;
out->owner = THIS_MODULE;
omapdss_register_output(out);
}
static void __exit hdmi_uninit_output(struct platform_device *pdev)
{
struct omap_dss_device *out = &hdmi.output;
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;
r = hdmi_pll_init(pdev, &hdmi.pll);
if (r)
return r;
r = hdmi_phy_init(pdev, &hdmi.phy);
if (r)
return r;
r = hdmi5_core_init(pdev, &hdmi.core);
if (r)
return r;
r = hdmi_get_clocks(pdev);
if (r) {
DSSERR("can't get clocks\n");
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);
dss_debugfs_create_file("hdmi", hdmi_dump_regs);
return 0;
}
static int __exit omapdss_hdmihw_remove(struct platform_device *pdev)
{
hdmi_uninit_output(pdev);
pm_runtime_disable(&pdev->dev);
return 0;
}
static int hdmi_runtime_suspend(struct device *dev)
{
clk_disable_unprepare(hdmi.sys_clk);
dispc_runtime_put();
return 0;
}
static int hdmi_runtime_resume(struct device *dev)
{
int r;
r = dispc_runtime_get();
if (r < 0)
return r;
clk_prepare_enable(hdmi.sys_clk);
return 0;
}
static const struct dev_pm_ops hdmi_pm_ops = {
.runtime_suspend = hdmi_runtime_suspend,
.runtime_resume = hdmi_runtime_resume,
};
static const struct of_device_id hdmi_of_match[] = {
{ .compatible = "ti,omap5-hdmi", },
{},
};
static struct platform_driver omapdss_hdmihw_driver = {
.probe = omapdss_hdmihw_probe,
.remove = __exit_p(omapdss_hdmihw_remove),
.driver = {
.name = "omapdss_hdmi5",
.owner = THIS_MODULE,
.pm = &hdmi_pm_ops,
.of_match_table = hdmi_of_match,
},
};
int __init hdmi5_init_platform_driver(void)
{
return platform_driver_register(&omapdss_hdmihw_driver);
}
void __exit hdmi5_uninit_platform_driver(void)
{
platform_driver_unregister(&omapdss_hdmihw_driver);
}
/*
* OMAP5 HDMI CORE IP driver library
*
* Copyright (C) 2014 Texas Instruments Incorporated
*
* Authors:
* Yong Zhi
* Mythri pk
* Archit Taneja <archit@ti.com>
* 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/>.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/seq_file.h>
#include <drm/drm_edid.h>
#if defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
#include <sound/asound.h>
#include <sound/asoundef.h>
#endif
#include "hdmi5_core.h"
/* only 24 bit color depth used for now */
static const struct csc_table csc_table_deepcolor[] = {
/* HDMI_DEEP_COLOR_24BIT */
[0] = { 7036, 0, 0, 32, 0, 7036, 0, 32, 0, 0, 7036, 32, },
/* HDMI_DEEP_COLOR_30BIT */
[1] = { 7015, 0, 0, 128, 0, 7015, 0, 128, 0, 0, 7015, 128, },
/* HDMI_DEEP_COLOR_36BIT */
[2] = { 7010, 0, 0, 512, 0, 7010, 0, 512, 0, 0, 7010, 512, },
/* FULL RANGE */
[3] = { 8192, 0, 0, 0, 0, 8192, 0, 0, 0, 0, 8192, 0, },
};
static void hdmi_core_ddc_init(struct hdmi_core_data *core)
{
void __iomem *base = core->base;
const unsigned long long iclk = 266000000; /* DSS L3 ICLK */
const unsigned ss_scl_high = 4000; /* ns */
const unsigned ss_scl_low = 4700; /* ns */
const unsigned fs_scl_high = 600; /* ns */
const unsigned fs_scl_low = 1300; /* ns */
const unsigned sda_hold = 300; /* ns */
const unsigned sfr_div = 10;
unsigned long long sfr;
unsigned v;
sfr = iclk / sfr_div; /* SFR_DIV */
sfr /= 1000; /* SFR clock in kHz */
/* Reset */
REG_FLD_MOD(base, HDMI_CORE_I2CM_SOFTRSTZ, 0, 0, 0);
if (hdmi_wait_for_bit_change(base, HDMI_CORE_I2CM_SOFTRSTZ,
0, 0, 1) != 1)
DSSERR("HDMI I2CM reset failed\n");
/* Standard (0) or Fast (1) Mode */
REG_FLD_MOD(base, HDMI_CORE_I2CM_DIV, 0, 3, 3);
/* Standard Mode SCL High counter */
v = DIV_ROUND_UP_ULL(ss_scl_high * sfr, 1000000);
REG_FLD_MOD(base, HDMI_CORE_I2CM_SS_SCL_HCNT_1_ADDR,
(v >> 8) & 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_I2CM_SS_SCL_HCNT_0_ADDR,
v & 0xff, 7, 0);
/* Standard Mode SCL Low counter */
v = DIV_ROUND_UP_ULL(ss_scl_low * sfr, 1000000);
REG_FLD_MOD(base, HDMI_CORE_I2CM_SS_SCL_LCNT_1_ADDR,
(v >> 8) & 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_I2CM_SS_SCL_LCNT_0_ADDR,
v & 0xff, 7, 0);
/* Fast Mode SCL High Counter */
v = DIV_ROUND_UP_ULL(fs_scl_high * sfr, 1000000);
REG_FLD_MOD(base, HDMI_CORE_I2CM_FS_SCL_HCNT_1_ADDR,
(v >> 8) & 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_I2CM_FS_SCL_HCNT_0_ADDR,
v & 0xff, 7, 0);
/* Fast Mode SCL Low Counter */
v = DIV_ROUND_UP_ULL(fs_scl_low * sfr, 1000000);
REG_FLD_MOD(base, HDMI_CORE_I2CM_FS_SCL_LCNT_1_ADDR,
(v >> 8) & 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_I2CM_FS_SCL_LCNT_0_ADDR,
v & 0xff, 7, 0);
/* SDA Hold Time */
v = DIV_ROUND_UP_ULL(sda_hold * sfr, 1000000);
REG_FLD_MOD(base, HDMI_CORE_I2CM_SDA_HOLD_ADDR, v & 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_I2CM_SLAVE, 0x50, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_I2CM_SEGADDR, 0x30, 6, 0);
/* NACK_POL to high */
REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 7, 7);
/* NACK_MASK to unmasked */
REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x0, 6, 6);
/* ARBITRATION_POL to high */
REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 3, 3);
/* ARBITRATION_MASK to unmasked */
REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x0, 2, 2);
/* DONE_POL to high */
REG_FLD_MOD(base, HDMI_CORE_I2CM_INT, 0x1, 3, 3);
/* DONE_MASK to unmasked */
REG_FLD_MOD(base, HDMI_CORE_I2CM_INT, 0x0, 2, 2);
}
static void hdmi_core_ddc_uninit(struct hdmi_core_data *core)
{
void __iomem *base = core->base;
/* Mask I2C interrupts */
REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 6, 6);
REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 2, 2);
REG_FLD_MOD(base, HDMI_CORE_I2CM_INT, 0x1, 2, 2);
}
static int hdmi_core_ddc_edid(struct hdmi_core_data *core, u8 *pedid, u8 ext)
{
void __iomem *base = core->base;
u8 cur_addr;
char checksum = 0;
const int retries = 1000;
u8 seg_ptr = ext / 2;
u8 edidbase = ((ext % 2) * 0x80);
REG_FLD_MOD(base, HDMI_CORE_I2CM_SEGPTR, seg_ptr, 7, 0);
/*
* TODO: We use polling here, although we probably should use proper
* interrupts.
*/
for (cur_addr = 0; cur_addr < 128; ++cur_addr) {
int i;
/* clear ERROR and DONE */
REG_FLD_MOD(base, HDMI_CORE_IH_I2CM_STAT0, 0x3, 1, 0);
REG_FLD_MOD(base, HDMI_CORE_I2CM_ADDRESS,
edidbase + cur_addr, 7, 0);
if (seg_ptr)
REG_FLD_MOD(base, HDMI_CORE_I2CM_OPERATION, 1, 1, 1);
else
REG_FLD_MOD(base, HDMI_CORE_I2CM_OPERATION, 1, 0, 0);
for (i = 0; i < retries; ++i) {
u32 stat;
stat = REG_GET(base, HDMI_CORE_IH_I2CM_STAT0, 1, 0);
/* I2CM_ERROR */
if (stat & 1) {
DSSERR("HDMI I2C Master Error\n");
return -EIO;
}
/* I2CM_DONE */
if (stat & (1 << 1))
break;
usleep_range(250, 1000);
}
if (i == retries) {
DSSERR("HDMI I2C timeout reading EDID\n");
return -EIO;
}
pedid[cur_addr] = REG_GET(base, HDMI_CORE_I2CM_DATAI, 7, 0);
checksum += pedid[cur_addr];
}
return 0;
}
int hdmi5_read_edid(struct hdmi_core_data *core, u8 *edid, int len)
{
int r, n, i;
int max_ext_blocks = (len / 128) - 1;
if (len < 128)
return -EINVAL;
hdmi_core_ddc_init(core);
r = hdmi_core_ddc_edid(core, edid, 0);
if (r)
goto out;
n = edid[0x7e];
if (n > max_ext_blocks)
n = max_ext_blocks;
for (i = 1; i <= n; i++) {
r = hdmi_core_ddc_edid(core, edid + i * EDID_LENGTH, i);
if (r)
goto out;
}
out:
hdmi_core_ddc_uninit(core);
return r ? r : len;
}
void hdmi5_core_dump(struct hdmi_core_data *core, struct seq_file *s)
{
#define DUMPCORE(r) seq_printf(s, "%-35s %08x\n", #r,\
hdmi_read_reg(core->base, r))
DUMPCORE(HDMI_CORE_FC_INVIDCONF);
DUMPCORE(HDMI_CORE_FC_INHACTIV0);
DUMPCORE(HDMI_CORE_FC_INHACTIV1);
DUMPCORE(HDMI_CORE_FC_INHBLANK0);
DUMPCORE(HDMI_CORE_FC_INHBLANK1);
DUMPCORE(HDMI_CORE_FC_INVACTIV0);
DUMPCORE(HDMI_CORE_FC_INVACTIV1);
DUMPCORE(HDMI_CORE_FC_INVBLANK);
DUMPCORE(HDMI_CORE_FC_HSYNCINDELAY0);
DUMPCORE(HDMI_CORE_FC_HSYNCINDELAY1);
DUMPCORE(HDMI_CORE_FC_HSYNCINWIDTH0);
DUMPCORE(HDMI_CORE_FC_HSYNCINWIDTH1);
DUMPCORE(HDMI_CORE_FC_VSYNCINDELAY);
DUMPCORE(HDMI_CORE_FC_VSYNCINWIDTH);
DUMPCORE(HDMI_CORE_FC_CTRLDUR);
DUMPCORE(HDMI_CORE_FC_EXCTRLDUR);
DUMPCORE(HDMI_CORE_FC_EXCTRLSPAC);
DUMPCORE(HDMI_CORE_FC_CH0PREAM);
DUMPCORE(HDMI_CORE_FC_CH1PREAM);
DUMPCORE(HDMI_CORE_FC_CH2PREAM);
DUMPCORE(HDMI_CORE_FC_AVICONF0);
DUMPCORE(HDMI_CORE_FC_AVICONF1);
DUMPCORE(HDMI_CORE_FC_AVICONF2);
DUMPCORE(HDMI_CORE_FC_AVIVID);
DUMPCORE(HDMI_CORE_FC_PRCONF);
DUMPCORE(HDMI_CORE_MC_CLKDIS);
DUMPCORE(HDMI_CORE_MC_SWRSTZREQ);
DUMPCORE(HDMI_CORE_MC_FLOWCTRL);
DUMPCORE(HDMI_CORE_MC_PHYRSTZ);
DUMPCORE(HDMI_CORE_MC_LOCKONCLOCK);
DUMPCORE(HDMI_CORE_I2CM_SLAVE);
DUMPCORE(HDMI_CORE_I2CM_ADDRESS);
DUMPCORE(HDMI_CORE_I2CM_DATAO);
DUMPCORE(HDMI_CORE_I2CM_DATAI);
DUMPCORE(HDMI_CORE_I2CM_OPERATION);
DUMPCORE(HDMI_CORE_I2CM_INT);
DUMPCORE(HDMI_CORE_I2CM_CTLINT);
DUMPCORE(HDMI_CORE_I2CM_DIV);
DUMPCORE(HDMI_CORE_I2CM_SEGADDR);
DUMPCORE(HDMI_CORE_I2CM_SOFTRSTZ);
DUMPCORE(HDMI_CORE_I2CM_SEGPTR);
DUMPCORE(HDMI_CORE_I2CM_SS_SCL_HCNT_1_ADDR);
DUMPCORE(HDMI_CORE_I2CM_SS_SCL_HCNT_0_ADDR);
DUMPCORE(HDMI_CORE_I2CM_SS_SCL_LCNT_1_ADDR);
DUMPCORE(HDMI_CORE_I2CM_SS_SCL_LCNT_0_ADDR);
DUMPCORE(HDMI_CORE_I2CM_FS_SCL_HCNT_1_ADDR);
DUMPCORE(HDMI_CORE_I2CM_FS_SCL_HCNT_0_ADDR);
DUMPCORE(HDMI_CORE_I2CM_FS_SCL_LCNT_1_ADDR);
DUMPCORE(HDMI_CORE_I2CM_FS_SCL_LCNT_0_ADDR);
DUMPCORE(HDMI_CORE_I2CM_SDA_HOLD_ADDR);
}
static void hdmi_core_init(struct hdmi_core_vid_config *video_cfg,
struct hdmi_core_infoframe_avi *avi_cfg,
struct hdmi_config *cfg)
{
DSSDBG("hdmi_core_init\n");
/* video core */
video_cfg->data_enable_pol = 1; /* It is always 1*/
video_cfg->v_fc_config.timings.hsync_level = cfg->timings.hsync_level;
video_cfg->v_fc_config.timings.x_res = cfg->timings.x_res;
video_cfg->v_fc_config.timings.hsw = cfg->timings.hsw - 1;
video_cfg->v_fc_config.timings.hbp = cfg->timings.hbp;
video_cfg->v_fc_config.timings.hfp = cfg->timings.hfp;
video_cfg->hblank = cfg->timings.hfp +
cfg->timings.hbp + cfg->timings.hsw - 1;
video_cfg->v_fc_config.timings.vsync_level = cfg->timings.vsync_level;
video_cfg->v_fc_config.timings.y_res = cfg->timings.y_res;
video_cfg->v_fc_config.timings.vsw = cfg->timings.vsw;
video_cfg->v_fc_config.timings.vfp = cfg->timings.vfp;
video_cfg->v_fc_config.timings.vbp = cfg->timings.vbp;
video_cfg->vblank_osc = 0; /* Always 0 - need to confirm */
video_cfg->vblank = cfg->timings.vsw +
cfg->timings.vfp + cfg->timings.vbp;
video_cfg->v_fc_config.cm.mode = cfg->cm.mode;
video_cfg->v_fc_config.timings.interlace = cfg->timings.interlace;
/* info frame */
avi_cfg->db1_format = 0;
avi_cfg->db1_active_info = 0;
avi_cfg->db1_bar_info_dv = 0;
avi_cfg->db1_scan_info = 0;
avi_cfg->db2_colorimetry = 0;
avi_cfg->db2_aspect_ratio = 0;
avi_cfg->db2_active_fmt_ar = 0;
avi_cfg->db3_itc = 0;
avi_cfg->db3_ec = 0;
avi_cfg->db3_q_range = 0;
avi_cfg->db3_nup_scaling = 0;
avi_cfg->db4_videocode = 0;
avi_cfg->db5_pixel_repeat = 0;
avi_cfg->db6_7_line_eoftop = 0;
avi_cfg->db8_9_line_sofbottom = 0;
avi_cfg->db10_11_pixel_eofleft = 0;
avi_cfg->db12_13_pixel_sofright = 0;
}
/* DSS_HDMI_CORE_VIDEO_CONFIG */
static void hdmi_core_video_config(struct hdmi_core_data *core,
struct hdmi_core_vid_config *cfg)
{
void __iomem *base = core->base;
unsigned char r = 0;
bool vsync_pol, hsync_pol;
vsync_pol =
cfg->v_fc_config.timings.vsync_level == OMAPDSS_SIG_ACTIVE_HIGH;
hsync_pol =
cfg->v_fc_config.timings.hsync_level == OMAPDSS_SIG_ACTIVE_HIGH;
/* Set hsync, vsync and data-enable polarity */
r = hdmi_read_reg(base, HDMI_CORE_FC_INVIDCONF);
r = FLD_MOD(r, vsync_pol, 6, 6);
r = FLD_MOD(r, hsync_pol, 5, 5);
r = FLD_MOD(r, cfg->data_enable_pol, 4, 4);
r = FLD_MOD(r, cfg->vblank_osc, 1, 1);
r = FLD_MOD(r, cfg->v_fc_config.timings.interlace, 0, 0);
hdmi_write_reg(base, HDMI_CORE_FC_INVIDCONF, r);
/* set x resolution */
REG_FLD_MOD(base, HDMI_CORE_FC_INHACTIV1,
cfg->v_fc_config.timings.x_res >> 8, 4, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_INHACTIV0,
cfg->v_fc_config.timings.x_res & 0xFF, 7, 0);
/* set y resolution */
REG_FLD_MOD(base, HDMI_CORE_FC_INVACTIV1,
cfg->v_fc_config.timings.y_res >> 8, 4, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_INVACTIV0,
cfg->v_fc_config.timings.y_res & 0xFF, 7, 0);
/* set horizontal blanking pixels */
REG_FLD_MOD(base, HDMI_CORE_FC_INHBLANK1, cfg->hblank >> 8, 4, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_INHBLANK0, cfg->hblank & 0xFF, 7, 0);
/* set vertial blanking pixels */
REG_FLD_MOD(base, HDMI_CORE_FC_INVBLANK, cfg->vblank, 7, 0);
/* set horizontal sync offset */
REG_FLD_MOD(base, HDMI_CORE_FC_HSYNCINDELAY1,
cfg->v_fc_config.timings.hfp >> 8, 4, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_HSYNCINDELAY0,
cfg->v_fc_config.timings.hfp & 0xFF, 7, 0);
/* set vertical sync offset */
REG_FLD_MOD(base, HDMI_CORE_FC_VSYNCINDELAY,
cfg->v_fc_config.timings.vfp, 7, 0);
/* set horizontal sync pulse width */
REG_FLD_MOD(base, HDMI_CORE_FC_HSYNCINWIDTH1,
(cfg->v_fc_config.timings.hsw >> 8), 1, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_HSYNCINWIDTH0,
cfg->v_fc_config.timings.hsw & 0xFF, 7, 0);
/* set vertical sync pulse width */
REG_FLD_MOD(base, HDMI_CORE_FC_VSYNCINWIDTH,
cfg->v_fc_config.timings.vsw, 5, 0);
/* select DVI mode */
REG_FLD_MOD(base, HDMI_CORE_FC_INVIDCONF,
cfg->v_fc_config.cm.mode, 3, 3);
}
static void hdmi_core_config_video_packetizer(struct hdmi_core_data *core)
{
void __iomem *base = core->base;
int clr_depth = 0; /* 24 bit color depth */
/* COLOR_DEPTH */
REG_FLD_MOD(base, HDMI_CORE_VP_PR_CD, clr_depth, 7, 4);
/* BYPASS_EN */
REG_FLD_MOD(base, HDMI_CORE_VP_CONF, clr_depth ? 0 : 1, 6, 6);
/* PP_EN */
REG_FLD_MOD(base, HDMI_CORE_VP_CONF, clr_depth ? 1 : 0, 5, 5);
/* YCC422_EN */
REG_FLD_MOD(base, HDMI_CORE_VP_CONF, 0, 3, 3);
/* PP_STUFFING */
REG_FLD_MOD(base, HDMI_CORE_VP_STUFF, clr_depth ? 1 : 0, 1, 1);
/* YCC422_STUFFING */
REG_FLD_MOD(base, HDMI_CORE_VP_STUFF, 1, 2, 2);
/* OUTPUT_SELECTOR */
REG_FLD_MOD(base, HDMI_CORE_VP_CONF, clr_depth ? 0 : 2, 1, 0);
}
static void hdmi_core_config_csc(struct hdmi_core_data *core)
{
int clr_depth = 0; /* 24 bit color depth */
/* CSC_COLORDEPTH */
REG_FLD_MOD(core->base, HDMI_CORE_CSC_SCALE, clr_depth, 7, 4);
}
static void hdmi_core_config_video_sampler(struct hdmi_core_data *core)
{
int video_mapping = 1; /* for 24 bit color depth */
/* VIDEO_MAPPING */
REG_FLD_MOD(core->base, HDMI_CORE_TX_INVID0, video_mapping, 4, 0);
}
static void hdmi_core_aux_infoframe_avi_config(struct hdmi_core_data *core)
{
void __iomem *base = core->base;
struct hdmi_core_infoframe_avi avi = core->avi_cfg;
REG_FLD_MOD(base, HDMI_CORE_FC_AVICONF0, avi.db1_format, 1, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_AVICONF0, avi.db1_active_info, 6, 6);
REG_FLD_MOD(base, HDMI_CORE_FC_AVICONF0, avi.db1_bar_info_dv, 3, 2);
REG_FLD_MOD(base, HDMI_CORE_FC_AVICONF0, avi.db1_scan_info, 5, 4);
REG_FLD_MOD(base, HDMI_CORE_FC_AVICONF1, avi.db2_colorimetry, 7, 6);
REG_FLD_MOD(base, HDMI_CORE_FC_AVICONF1, avi.db2_aspect_ratio, 5, 4);
REG_FLD_MOD(base, HDMI_CORE_FC_AVICONF1, avi.db2_active_fmt_ar, 3, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_AVICONF2, avi.db3_itc, 7, 7);
REG_FLD_MOD(base, HDMI_CORE_FC_AVICONF2, avi.db3_ec, 6, 4);
REG_FLD_MOD(base, HDMI_CORE_FC_AVICONF2, avi.db3_q_range, 3, 2);
REG_FLD_MOD(base, HDMI_CORE_FC_AVICONF2, avi.db3_nup_scaling, 1, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_AVIVID, avi.db4_videocode, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_PRCONF, avi.db5_pixel_repeat, 3, 0);
}
static void hdmi_core_csc_config(struct hdmi_core_data *core,
struct csc_table csc_coeff)
{
void __iomem *base = core->base;
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A1_MSB, csc_coeff.a1 >> 8 , 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A1_LSB, csc_coeff.a1, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A2_MSB, csc_coeff.a2 >> 8, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A2_LSB, csc_coeff.a2, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A3_MSB, csc_coeff.a3 >> 8, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A3_LSB, csc_coeff.a3, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A4_MSB, csc_coeff.a4 >> 8, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A4_LSB, csc_coeff.a4, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B1_MSB, csc_coeff.b1 >> 8, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B1_LSB, csc_coeff.b1, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B2_MSB, csc_coeff.b2 >> 8, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B2_LSB, csc_coeff.b2, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B3_MSB, csc_coeff.b3 >> 8, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B3_LSB, csc_coeff.b3, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B4_MSB, csc_coeff.b4 >> 8, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B4_LSB, csc_coeff.b4, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C1_MSB, csc_coeff.c1 >> 8, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C1_LSB, csc_coeff.c1, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C2_MSB, csc_coeff.c2 >> 8, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C2_LSB, csc_coeff.c2, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C3_MSB, csc_coeff.c3 >> 8, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C3_LSB, csc_coeff.c3, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C4_MSB, csc_coeff.c4 >> 8, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C4_LSB, csc_coeff.c4, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_MC_FLOWCTRL, 0x1, 0, 0);
}
static void hdmi_core_configure_range(struct hdmi_core_data *core)
{
struct csc_table csc_coeff = { 0 };
/* support limited range with 24 bit color depth for now */
csc_coeff = csc_table_deepcolor[0];
core->avi_cfg.db3_q_range = HDMI_INFOFRAME_AVI_DB3Q_LR;
hdmi_core_csc_config(core, csc_coeff);
hdmi_core_aux_infoframe_avi_config(core);
}
static void hdmi_core_enable_video_path(struct hdmi_core_data *core)
{
void __iomem *base = core->base;
DSSDBG("hdmi_core_enable_video_path\n");
REG_FLD_MOD(base, HDMI_CORE_FC_CTRLDUR, 0x0C, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_EXCTRLDUR, 0x20, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_EXCTRLSPAC, 0x01, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_CH0PREAM, 0x0B, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_CH1PREAM, 0x16, 5, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_CH2PREAM, 0x21, 5, 0);
REG_FLD_MOD(base, HDMI_CORE_MC_CLKDIS, 0x00, 0, 0);
REG_FLD_MOD(base, HDMI_CORE_MC_CLKDIS, 0x00, 1, 1);
}
static void hdmi_core_mask_interrupts(struct hdmi_core_data *core)
{
void __iomem *base = core->base;
/* Master IRQ mask */
REG_FLD_MOD(base, HDMI_CORE_IH_MUTE, 0x3, 1, 0);
/* Mask all the interrupts in HDMI core */
REG_FLD_MOD(base, HDMI_CORE_VP_MASK, 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_MASK0, 0xe7, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_MASK1, 0xfb, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_MASK2, 0x3, 1, 0);
REG_FLD_MOD(base, HDMI_CORE_AUD_INT, 0x3, 3, 2);
REG_FLD_MOD(base, HDMI_CORE_AUD_GP_MASK, 0x3, 1, 0);
REG_FLD_MOD(base, HDMI_CORE_CEC_MASK, 0x7f, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 6, 6);
REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 2, 2);
REG_FLD_MOD(base, HDMI_CORE_I2CM_INT, 0x1, 2, 2);
REG_FLD_MOD(base, HDMI_CORE_PHY_MASK0, 0xf3, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_PHY_STAT0, 0xff, 7, 0);
/* Clear all the current interrupt bits */
REG_FLD_MOD(base, HDMI_CORE_IH_VP_STAT0, 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT0, 0xe7, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT1, 0xfb, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT2, 0x3, 1, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_AS_STAT0, 0x7, 2, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_CEC_STAT0, 0x7f, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_I2CM_STAT0, 0x3, 1, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_PHY_STAT0, 0xff, 7, 0);
}
static void hdmi_core_enable_interrupts(struct hdmi_core_data *core)
{
/* Unmute interrupts */
REG_FLD_MOD(core->base, HDMI_CORE_IH_MUTE, 0x0, 1, 0);
}
int hdmi5_core_handle_irqs(struct hdmi_core_data *core)
{
void __iomem *base = core->base;
REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT0, 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT1, 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT2, 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_AS_STAT0, 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_PHY_STAT0, 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_I2CM_STAT0, 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_CEC_STAT0, 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_VP_STAT0, 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_I2CMPHY_STAT0, 0xff, 7, 0);
return 0;
}
void hdmi5_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
struct hdmi_config *cfg)
{
struct omap_video_timings video_timing;
struct hdmi_video_format video_format;
struct hdmi_core_vid_config v_core_cfg;
struct hdmi_core_infoframe_avi *avi_cfg = &core->avi_cfg;
hdmi_core_mask_interrupts(core);
hdmi_core_init(&v_core_cfg, avi_cfg, cfg);
hdmi_wp_init_vid_fmt_timings(&video_format, &video_timing, cfg);
hdmi_wp_video_config_timing(wp, &video_timing);
/* video config */
video_format.packing_mode = HDMI_PACK_24b_RGB_YUV444_YUV422;
hdmi_wp_video_config_format(wp, &video_format);
hdmi_wp_video_config_interface(wp, &video_timing);
hdmi_core_configure_range(core);
/*
* configure core video part, set software reset in the core
*/
v_core_cfg.packet_mode = HDMI_PACKETMODE24BITPERPIXEL;
hdmi_core_video_config(core, &v_core_cfg);
hdmi_core_config_video_packetizer(core);
hdmi_core_config_csc(core);
hdmi_core_config_video_sampler(core);
/*
* configure packet info frame video see doc CEA861-D page 65
*/
avi_cfg->db1_format = HDMI_INFOFRAME_AVI_DB1Y_RGB;
avi_cfg->db1_active_info =
HDMI_INFOFRAME_AVI_DB1A_ACTIVE_FORMAT_OFF;
avi_cfg->db1_bar_info_dv = HDMI_INFOFRAME_AVI_DB1B_NO;
avi_cfg->db1_scan_info = HDMI_INFOFRAME_AVI_DB1S_0;
avi_cfg->db2_colorimetry = HDMI_INFOFRAME_AVI_DB2C_NO;
avi_cfg->db2_aspect_ratio = HDMI_INFOFRAME_AVI_DB2M_NO;
avi_cfg->db2_active_fmt_ar = HDMI_INFOFRAME_AVI_DB2R_SAME;
avi_cfg->db3_itc = HDMI_INFOFRAME_AVI_DB3ITC_NO;
avi_cfg->db3_ec = HDMI_INFOFRAME_AVI_DB3EC_XVYUV601;
avi_cfg->db3_q_range = HDMI_INFOFRAME_AVI_DB3Q_DEFAULT;
avi_cfg->db3_nup_scaling = HDMI_INFOFRAME_AVI_DB3SC_NO;
avi_cfg->db4_videocode = cfg->cm.code;
avi_cfg->db5_pixel_repeat = HDMI_INFOFRAME_AVI_DB5PR_NO;
avi_cfg->db6_7_line_eoftop = 0;
avi_cfg->db8_9_line_sofbottom = 0;
avi_cfg->db10_11_pixel_eofleft = 0;
avi_cfg->db12_13_pixel_sofright = 0;
hdmi_core_aux_infoframe_avi_config(core);
hdmi_core_enable_video_path(core);
hdmi_core_enable_interrupts(core);
}
#if defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
static void hdmi5_core_audio_config(struct hdmi_core_data *core,
struct hdmi_core_audio_config *cfg)
{
void __iomem *base = core->base;
u8 val;
/* Mute audio before configuring */
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCONF, 0xf, 7, 4);
/* Set the N parameter */
REG_FLD_MOD(base, HDMI_CORE_AUD_N1, cfg->n, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_AUD_N2, cfg->n >> 8, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_AUD_N3, cfg->n >> 16, 3, 0);
/*
* CTS manual mode. Automatic mode is not supported when using audio
* parallel interface.
*/
REG_FLD_MOD(base, HDMI_CORE_AUD_CTS3, 1, 4, 4);
REG_FLD_MOD(base, HDMI_CORE_AUD_CTS1, cfg->cts, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_AUD_CTS2, cfg->cts >> 8, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_AUD_CTS3, cfg->cts >> 16, 3, 0);
/* Layout of Audio Sample Packets: 2-channel or multichannels */
if (cfg->layout == HDMI_AUDIO_LAYOUT_2CH)
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCONF, 0, 0, 0);
else
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCONF, 1, 0, 0);
/* Configure IEC-609580 Validity bits */
/* Channel 0 is valid */
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, 0, 0, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, 0, 4, 4);
if (cfg->layout == HDMI_AUDIO_LAYOUT_2CH)
val = 1;
else
val = 0;
/* Channels 1, 2 setting */
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 1, 1);
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 5, 5);
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 2, 2);
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 6, 6);
/* Channel 3 setting */
if (cfg->layout == HDMI_AUDIO_LAYOUT_6CH)
val = 1;
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 3, 3);
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 7, 7);
/* Configure IEC-60958 User bits */
/* TODO: should be set by user. */
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSU, 0, 7, 0);
/* Configure IEC-60958 Channel Status word */
/* CGMSA */
val = cfg->iec60958_cfg->status[5] & IEC958_AES5_CON_CGMSA;
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(0), val, 5, 4);
/* Copyright */
val = (cfg->iec60958_cfg->status[0] &
IEC958_AES0_CON_NOT_COPYRIGHT) >> 2;
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(0), val, 0, 0);
/* Category */
hdmi_write_reg(base, HDMI_CORE_FC_AUDSCHNLS(1),
cfg->iec60958_cfg->status[1]);
/* PCM audio mode */
val = (cfg->iec60958_cfg->status[0] & IEC958_AES0_CON_MODE) >> 6;
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(2), val, 6, 4);
/* Source number */
val = cfg->iec60958_cfg->status[2] & IEC958_AES2_CON_SOURCE;
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(2), val, 3, 4);
/* Channel number right 0 */
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(3), 2, 3, 0);
/* Channel number right 1*/
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(3), 4, 7, 4);
/* Channel number right 2 */
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(4), 6, 3, 0);
/* Channel number right 3*/
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(4), 8, 7, 4);
/* Channel number left 0 */
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(5), 1, 3, 0);
/* Channel number left 1*/
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(5), 3, 7, 4);
/* Channel number left 2 */
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(6), 5, 3, 0);
/* Channel number left 3*/
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(6), 7, 7, 4);
/* Clock accuracy and sample rate */
hdmi_write_reg(base, HDMI_CORE_FC_AUDSCHNLS(7),
cfg->iec60958_cfg->status[3]);
/* Original sample rate and word length */
hdmi_write_reg(base, HDMI_CORE_FC_AUDSCHNLS(8),
cfg->iec60958_cfg->status[4]);
/* Enable FIFO empty and full interrupts */
REG_FLD_MOD(base, HDMI_CORE_AUD_INT, 3, 3, 2);
/* Configure GPA */
/* select HBR/SPDIF interfaces */
if (cfg->layout == HDMI_AUDIO_LAYOUT_2CH) {
/* select HBR/SPDIF interfaces */
REG_FLD_MOD(base, HDMI_CORE_AUD_CONF0, 0, 5, 5);
/* enable two channels in GPA */
REG_FLD_MOD(base, HDMI_CORE_AUD_GP_CONF1, 3, 7, 0);
} else if (cfg->layout == HDMI_AUDIO_LAYOUT_6CH) {
/* select HBR/SPDIF interfaces */
REG_FLD_MOD(base, HDMI_CORE_AUD_CONF0, 0, 5, 5);
/* enable six channels in GPA */
REG_FLD_MOD(base, HDMI_CORE_AUD_GP_CONF1, 0x3F, 7, 0);
} else {
/* select HBR/SPDIF interfaces */
REG_FLD_MOD(base, HDMI_CORE_AUD_CONF0, 0, 5, 5);
/* enable eight channels in GPA */
REG_FLD_MOD(base, HDMI_CORE_AUD_GP_CONF1, 0xFF, 7, 0);
}
/* disable HBR */
REG_FLD_MOD(base, HDMI_CORE_AUD_GP_CONF2, 0, 0, 0);
/* enable PCUV */
REG_FLD_MOD(base, HDMI_CORE_AUD_GP_CONF2, 1, 1, 1);
/* enable GPA FIFO full and empty mask */
REG_FLD_MOD(base, HDMI_CORE_AUD_GP_MASK, 3, 1, 0);
/* set polarity of GPA FIFO empty interrupts */
REG_FLD_MOD(base, HDMI_CORE_AUD_GP_POL, 1, 0, 0);
/* unmute audio */
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCONF, 0, 7, 4);
}
static void hdmi5_core_audio_infoframe_cfg(struct hdmi_core_data *core,
struct snd_cea_861_aud_if *info_aud)
{
void __iomem *base = core->base;
/* channel count and coding type fields in AUDICONF0 are swapped */
hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF0,
(info_aud->db1_ct_cc & CEA861_AUDIO_INFOFRAME_DB1CC) << 4 |
(info_aud->db1_ct_cc & CEA861_AUDIO_INFOFRAME_DB1CT) >> 4);
hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF1, info_aud->db2_sf_ss);
hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF2, info_aud->db4_ca);
hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF3, info_aud->db5_dminh_lsv);
}
int hdmi5_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
struct omap_dss_audio *audio, u32 pclk)
{
struct hdmi_audio_format audio_format;
struct hdmi_audio_dma audio_dma;
struct hdmi_core_audio_config core_cfg;
int err, n, cts, channel_count;
unsigned int fs_nr;
bool word_length_16b = false;
if (!audio || !audio->iec || !audio->cea || !core)
return -EINVAL;
core_cfg.iec60958_cfg = audio->iec;
if (!(audio->iec->status[4] & IEC958_AES4_CON_MAX_WORDLEN_24) &&
(audio->iec->status[4] & IEC958_AES4_CON_WORDLEN_20_16))
word_length_16b = true;
/* only 16-bit word length supported atm */
if (!word_length_16b)
return -EINVAL;
switch (audio->iec->status[3] & IEC958_AES3_CON_FS) {
case IEC958_AES3_CON_FS_32000:
fs_nr = 32000;
break;
case IEC958_AES3_CON_FS_44100:
fs_nr = 44100;
break;
case IEC958_AES3_CON_FS_48000:
fs_nr = 48000;
break;
case IEC958_AES3_CON_FS_88200:
fs_nr = 88200;
break;
case IEC958_AES3_CON_FS_96000:
fs_nr = 96000;
break;
case IEC958_AES3_CON_FS_176400:
fs_nr = 176400;
break;
case IEC958_AES3_CON_FS_192000:
fs_nr = 192000;
break;
default:
return -EINVAL;
}
err = hdmi_compute_acr(pclk, fs_nr, &n, &cts);
core_cfg.n = n;
core_cfg.cts = cts;
/* Audio channels settings */
channel_count = (audio->cea->db1_ct_cc & CEA861_AUDIO_INFOFRAME_DB1CC)
+ 1;
if (channel_count == 2)
core_cfg.layout = HDMI_AUDIO_LAYOUT_2CH;
else if (channel_count == 6)
core_cfg.layout = HDMI_AUDIO_LAYOUT_6CH;
else
core_cfg.layout = HDMI_AUDIO_LAYOUT_8CH;
/* DMA settings */
if (word_length_16b)
audio_dma.transfer_size = 0x10;
else
audio_dma.transfer_size = 0x20;
audio_dma.block_size = 0xC0;
audio_dma.mode = HDMI_AUDIO_TRANSF_DMA;
audio_dma.fifo_threshold = 0x20; /* in number of samples */
/* audio FIFO format settings for 16-bit samples*/
audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_TWOSAMPLES;
audio_format.sample_size = HDMI_AUDIO_SAMPLE_16BITS;
audio_format.justification = HDMI_AUDIO_JUSTIFY_LEFT;
/* only LPCM atm */
audio_format.type = HDMI_AUDIO_TYPE_LPCM;
/* disable start/stop signals of IEC 60958 blocks */
audio_format.en_sig_blk_strt_end = HDMI_AUDIO_BLOCK_SIG_STARTEND_ON;
/* configure DMA and audio FIFO format*/
hdmi_wp_audio_config_dma(wp, &audio_dma);
hdmi_wp_audio_config_format(wp, &audio_format);
/* configure the core */
hdmi5_core_audio_config(core, &core_cfg);
/* configure CEA 861 audio infoframe */
hdmi5_core_audio_infoframe_cfg(core, audio->cea);
return 0;
}
#endif
int hdmi5_core_init(struct platform_device *pdev, struct hdmi_core_data *core)
{
struct resource *res;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
if (!res) {
DSSERR("can't get CORE IORESOURCE_MEM HDMI\n");
return -EINVAL;
}
core->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(core->base)) {
DSSERR("can't ioremap HDMI core\n");
return PTR_ERR(core->base);
}
return 0;
}
/*
* HDMI driver definition for TI OMAP5 processors.
*
* Copyright (C) 2011-2012 Texas Instruments Incorporated - http://www.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/>.
*/
#ifndef _HDMI5_CORE_H_
#define _HDMI5_CORE_H_
#include "hdmi.h"
/* HDMI IP Core System */
/* HDMI Identification */
#define HDMI_CORE_DESIGN_ID 0x00000
#define HDMI_CORE_REVISION_ID 0x00004
#define HDMI_CORE_PRODUCT_ID0 0x00008
#define HDMI_CORE_PRODUCT_ID1 0x0000C
#define HDMI_CORE_CONFIG0_ID 0x00010
#define HDMI_CORE_CONFIG1_ID 0x00014
#define HDMI_CORE_CONFIG2_ID 0x00018
#define HDMI_CORE_CONFIG3_ID 0x0001C
/* HDMI Interrupt */
#define HDMI_CORE_IH_FC_STAT0 0x00400
#define HDMI_CORE_IH_FC_STAT1 0x00404
#define HDMI_CORE_IH_FC_STAT2 0x00408
#define HDMI_CORE_IH_AS_STAT0 0x0040C
#define HDMI_CORE_IH_PHY_STAT0 0x00410
#define HDMI_CORE_IH_I2CM_STAT0 0x00414
#define HDMI_CORE_IH_CEC_STAT0 0x00418
#define HDMI_CORE_IH_VP_STAT0 0x0041C
#define HDMI_CORE_IH_I2CMPHY_STAT0 0x00420
#define HDMI_CORE_IH_MUTE 0x007FC
/* HDMI Video Sampler */
#define HDMI_CORE_TX_INVID0 0x00800
#define HDMI_CORE_TX_INSTUFFING 0x00804
#define HDMI_CORE_TX_RGYDATA0 0x00808
#define HDMI_CORE_TX_RGYDATA1 0x0080C
#define HDMI_CORE_TX_RCRDATA0 0x00810
#define HDMI_CORE_TX_RCRDATA1 0x00814
#define HDMI_CORE_TX_BCBDATA0 0x00818
#define HDMI_CORE_TX_BCBDATA1 0x0081C
/* HDMI Video Packetizer */
#define HDMI_CORE_VP_STATUS 0x02000
#define HDMI_CORE_VP_PR_CD 0x02004
#define HDMI_CORE_VP_STUFF 0x02008
#define HDMI_CORE_VP_REMAP 0x0200C
#define HDMI_CORE_VP_CONF 0x02010
#define HDMI_CORE_VP_STAT 0x02014
#define HDMI_CORE_VP_INT 0x02018
#define HDMI_CORE_VP_MASK 0x0201C
#define HDMI_CORE_VP_POL 0x02020
/* Frame Composer */
#define HDMI_CORE_FC_INVIDCONF 0x04000
#define HDMI_CORE_FC_INHACTIV0 0x04004
#define HDMI_CORE_FC_INHACTIV1 0x04008
#define HDMI_CORE_FC_INHBLANK0 0x0400C
#define HDMI_CORE_FC_INHBLANK1 0x04010
#define HDMI_CORE_FC_INVACTIV0 0x04014
#define HDMI_CORE_FC_INVACTIV1 0x04018
#define HDMI_CORE_FC_INVBLANK 0x0401C
#define HDMI_CORE_FC_HSYNCINDELAY0 0x04020
#define HDMI_CORE_FC_HSYNCINDELAY1 0x04024
#define HDMI_CORE_FC_HSYNCINWIDTH0 0x04028
#define HDMI_CORE_FC_HSYNCINWIDTH1 0x0402C
#define HDMI_CORE_FC_VSYNCINDELAY 0x04030
#define HDMI_CORE_FC_VSYNCINWIDTH 0x04034
#define HDMI_CORE_FC_INFREQ0 0x04038
#define HDMI_CORE_FC_INFREQ1 0x0403C
#define HDMI_CORE_FC_INFREQ2 0x04040
#define HDMI_CORE_FC_CTRLDUR 0x04044
#define HDMI_CORE_FC_EXCTRLDUR 0x04048
#define HDMI_CORE_FC_EXCTRLSPAC 0x0404C
#define HDMI_CORE_FC_CH0PREAM 0x04050
#define HDMI_CORE_FC_CH1PREAM 0x04054
#define HDMI_CORE_FC_CH2PREAM 0x04058
#define HDMI_CORE_FC_AVICONF3 0x0405C
#define HDMI_CORE_FC_GCP 0x04060
#define HDMI_CORE_FC_AVICONF0 0x04064
#define HDMI_CORE_FC_AVICONF1 0x04068
#define HDMI_CORE_FC_AVICONF2 0x0406C
#define HDMI_CORE_FC_AVIVID 0x04070
#define HDMI_CORE_FC_AVIETB0 0x04074
#define HDMI_CORE_FC_AVIETB1 0x04078
#define HDMI_CORE_FC_AVISBB0 0x0407C
#define HDMI_CORE_FC_AVISBB1 0x04080
#define HDMI_CORE_FC_AVIELB0 0x04084
#define HDMI_CORE_FC_AVIELB1 0x04088
#define HDMI_CORE_FC_AVISRB0 0x0408C
#define HDMI_CORE_FC_AVISRB1 0x04090
#define HDMI_CORE_FC_AUDICONF0 0x04094
#define HDMI_CORE_FC_AUDICONF1 0x04098
#define HDMI_CORE_FC_AUDICONF2 0x0409C
#define HDMI_CORE_FC_AUDICONF3 0x040A0
#define HDMI_CORE_FC_VSDIEEEID0 0x040A4
#define HDMI_CORE_FC_VSDSIZE 0x040A8
#define HDMI_CORE_FC_VSDIEEEID1 0x040C0
#define HDMI_CORE_FC_VSDIEEEID2 0x040C4
#define HDMI_CORE_FC_VSDPAYLOAD(n) (n * 4 + 0x040C8)
#define HDMI_CORE_FC_SPDVENDORNAME(n) (n * 4 + 0x04128)
#define HDMI_CORE_FC_SPDPRODUCTNAME(n) (n * 4 + 0x04148)
#define HDMI_CORE_FC_SPDDEVICEINF 0x04188
#define HDMI_CORE_FC_AUDSCONF 0x0418C
#define HDMI_CORE_FC_AUDSSTAT 0x04190
#define HDMI_CORE_FC_AUDSV 0x04194
#define HDMI_CORE_FC_AUDSU 0x04198
#define HDMI_CORE_FC_AUDSCHNLS(n) (n * 4 + 0x0419C)
#define HDMI_CORE_FC_CTRLQHIGH 0x041CC
#define HDMI_CORE_FC_CTRLQLOW 0x041D0
#define HDMI_CORE_FC_ACP0 0x041D4
#define HDMI_CORE_FC_ACP(n) ((16-n) * 4 + 0x04208)
#define HDMI_CORE_FC_ISCR1_0 0x04248
#define HDMI_CORE_FC_ISCR1(n) ((16-n) * 4 + 0x0424C)
#define HDMI_CORE_FC_ISCR2(n) ((15-n) * 4 + 0x0428C)
#define HDMI_CORE_FC_DATAUTO0 0x042CC
#define HDMI_CORE_FC_DATAUTO1 0x042D0
#define HDMI_CORE_FC_DATAUTO2 0x042D4
#define HDMI_CORE_FC_DATMAN 0x042D8
#define HDMI_CORE_FC_DATAUTO3 0x042DC
#define HDMI_CORE_FC_RDRB(n) (n * 4 + 0x042E0)
#define HDMI_CORE_FC_STAT0 0x04340
#define HDMI_CORE_FC_INT0 0x04344
#define HDMI_CORE_FC_MASK0 0x04348
#define HDMI_CORE_FC_POL0 0x0434C
#define HDMI_CORE_FC_STAT1 0x04350
#define HDMI_CORE_FC_INT1 0x04354
#define HDMI_CORE_FC_MASK1 0x04358
#define HDMI_CORE_FC_POL1 0x0435C
#define HDMI_CORE_FC_STAT2 0x04360
#define HDMI_CORE_FC_INT2 0x04364
#define HDMI_CORE_FC_MASK2 0x04368
#define HDMI_CORE_FC_POL2 0x0436C
#define HDMI_CORE_FC_PRCONF 0x04380
#define HDMI_CORE_FC_GMD_STAT 0x04400
#define HDMI_CORE_FC_GMD_EN 0x04404
#define HDMI_CORE_FC_GMD_UP 0x04408
#define HDMI_CORE_FC_GMD_CONF 0x0440C
#define HDMI_CORE_FC_GMD_HB 0x04410
#define HDMI_CORE_FC_GMD_PB(n) (n * 4 + 0x04414)
#define HDMI_CORE_FC_DBGFORCE 0x04800
#define HDMI_CORE_FC_DBGAUD0CH0 0x04804
#define HDMI_CORE_FC_DBGAUD1CH0 0x04808
#define HDMI_CORE_FC_DBGAUD2CH0 0x0480C
#define HDMI_CORE_FC_DBGAUD0CH1 0x04810
#define HDMI_CORE_FC_DBGAUD1CH1 0x04814
#define HDMI_CORE_FC_DBGAUD2CH1 0x04818
#define HDMI_CORE_FC_DBGAUD0CH2 0x0481C
#define HDMI_CORE_FC_DBGAUD1CH2 0x04820
#define HDMI_CORE_FC_DBGAUD2CH2 0x04824
#define HDMI_CORE_FC_DBGAUD0CH3 0x04828
#define HDMI_CORE_FC_DBGAUD1CH3 0x0482C
#define HDMI_CORE_FC_DBGAUD2CH3 0x04830
#define HDMI_CORE_FC_DBGAUD0CH4 0x04834
#define HDMI_CORE_FC_DBGAUD1CH4 0x04838
#define HDMI_CORE_FC_DBGAUD2CH4 0x0483C
#define HDMI_CORE_FC_DBGAUD0CH5 0x04840
#define HDMI_CORE_FC_DBGAUD1CH5 0x04844
#define HDMI_CORE_FC_DBGAUD2CH5 0x04848
#define HDMI_CORE_FC_DBGAUD0CH6 0x0484C
#define HDMI_CORE_FC_DBGAUD1CH6 0x04850
#define HDMI_CORE_FC_DBGAUD2CH6 0x04854
#define HDMI_CORE_FC_DBGAUD0CH7 0x04858
#define HDMI_CORE_FC_DBGAUD1CH7 0x0485C
#define HDMI_CORE_FC_DBGAUD2CH7 0x04860
#define HDMI_CORE_FC_DBGTMDS0 0x04864
#define HDMI_CORE_FC_DBGTMDS1 0x04868
#define HDMI_CORE_FC_DBGTMDS2 0x0486C
#define HDMI_CORE_PHY_MASK0 0x0C018
#define HDMI_CORE_PHY_I2CM_INT_ADDR 0x0C09C
#define HDMI_CORE_PHY_I2CM_CTLINT_ADDR 0x0C0A0
/* HDMI Audio */
#define HDMI_CORE_AUD_CONF0 0x0C400
#define HDMI_CORE_AUD_CONF1 0x0C404
#define HDMI_CORE_AUD_INT 0x0C408
#define HDMI_CORE_AUD_N1 0x0C800
#define HDMI_CORE_AUD_N2 0x0C804
#define HDMI_CORE_AUD_N3 0x0C808
#define HDMI_CORE_AUD_CTS1 0x0C80C
#define HDMI_CORE_AUD_CTS2 0x0C810
#define HDMI_CORE_AUD_CTS3 0x0C814
#define HDMI_CORE_AUD_INCLKFS 0x0C818
#define HDMI_CORE_AUD_CC08 0x0CC08
#define HDMI_CORE_AUD_GP_CONF0 0x0D400
#define HDMI_CORE_AUD_GP_CONF1 0x0D404
#define HDMI_CORE_AUD_GP_CONF2 0x0D408
#define HDMI_CORE_AUD_D010 0x0D010
#define HDMI_CORE_AUD_GP_STAT 0x0D40C
#define HDMI_CORE_AUD_GP_INT 0x0D410
#define HDMI_CORE_AUD_GP_POL 0x0D414
#define HDMI_CORE_AUD_GP_MASK 0x0D418
/* HDMI Main Controller */
#define HDMI_CORE_MC_CLKDIS 0x10004
#define HDMI_CORE_MC_SWRSTZREQ 0x10008
#define HDMI_CORE_MC_FLOWCTRL 0x10010
#define HDMI_CORE_MC_PHYRSTZ 0x10014
#define HDMI_CORE_MC_LOCKONCLOCK 0x10018
/* HDMI COLOR SPACE CONVERTER */
#define HDMI_CORE_CSC_CFG 0x10400
#define HDMI_CORE_CSC_SCALE 0x10404
#define HDMI_CORE_CSC_COEF_A1_MSB 0x10408
#define HDMI_CORE_CSC_COEF_A1_LSB 0x1040C
#define HDMI_CORE_CSC_COEF_A2_MSB 0x10410
#define HDMI_CORE_CSC_COEF_A2_LSB 0x10414
#define HDMI_CORE_CSC_COEF_A3_MSB 0x10418
#define HDMI_CORE_CSC_COEF_A3_LSB 0x1041C
#define HDMI_CORE_CSC_COEF_A4_MSB 0x10420
#define HDMI_CORE_CSC_COEF_A4_LSB 0x10424
#define HDMI_CORE_CSC_COEF_B1_MSB 0x10428
#define HDMI_CORE_CSC_COEF_B1_LSB 0x1042C
#define HDMI_CORE_CSC_COEF_B2_MSB 0x10430
#define HDMI_CORE_CSC_COEF_B2_LSB 0x10434
#define HDMI_CORE_CSC_COEF_B3_MSB 0x10438
#define HDMI_CORE_CSC_COEF_B3_LSB 0x1043C
#define HDMI_CORE_CSC_COEF_B4_MSB 0x10440
#define HDMI_CORE_CSC_COEF_B4_LSB 0x10444
#define HDMI_CORE_CSC_COEF_C1_MSB 0x10448
#define HDMI_CORE_CSC_COEF_C1_LSB 0x1044C
#define HDMI_CORE_CSC_COEF_C2_MSB 0x10450
#define HDMI_CORE_CSC_COEF_C2_LSB 0x10454
#define HDMI_CORE_CSC_COEF_C3_MSB 0x10458
#define HDMI_CORE_CSC_COEF_C3_LSB 0x1045C
#define HDMI_CORE_CSC_COEF_C4_MSB 0x10460
#define HDMI_CORE_CSC_COEF_C4_LSB 0x10464
/* HDMI HDCP */
#define HDMI_CORE_HDCP_MASK 0x14020
/* HDMI CEC */
#define HDMI_CORE_CEC_MASK 0x17408
/* HDMI I2C Master */
#define HDMI_CORE_I2CM_SLAVE 0x157C8
#define HDMI_CORE_I2CM_ADDRESS 0x157CC
#define HDMI_CORE_I2CM_DATAO 0x157D0
#define HDMI_CORE_I2CM_DATAI 0X157D4
#define HDMI_CORE_I2CM_OPERATION 0x157D8
#define HDMI_CORE_I2CM_INT 0x157DC
#define HDMI_CORE_I2CM_CTLINT 0x157E0
#define HDMI_CORE_I2CM_DIV 0x157E4
#define HDMI_CORE_I2CM_SEGADDR 0x157E8
#define HDMI_CORE_I2CM_SOFTRSTZ 0x157EC
#define HDMI_CORE_I2CM_SEGPTR 0x157F0
#define HDMI_CORE_I2CM_SS_SCL_HCNT_1_ADDR 0x157F4
#define HDMI_CORE_I2CM_SS_SCL_HCNT_0_ADDR 0x157F8
#define HDMI_CORE_I2CM_SS_SCL_LCNT_1_ADDR 0x157FC
#define HDMI_CORE_I2CM_SS_SCL_LCNT_0_ADDR 0x15800
#define HDMI_CORE_I2CM_FS_SCL_HCNT_1_ADDR 0x15804
#define HDMI_CORE_I2CM_FS_SCL_HCNT_0_ADDR 0x15808
#define HDMI_CORE_I2CM_FS_SCL_LCNT_1_ADDR 0x1580C
#define HDMI_CORE_I2CM_FS_SCL_LCNT_0_ADDR 0x15810
#define HDMI_CORE_I2CM_SDA_HOLD_ADDR 0x15814
enum hdmi_core_packet_mode {
HDMI_PACKETMODERESERVEDVALUE = 0,
HDMI_PACKETMODE24BITPERPIXEL = 4,
HDMI_PACKETMODE30BITPERPIXEL = 5,
HDMI_PACKETMODE36BITPERPIXEL = 6,
HDMI_PACKETMODE48BITPERPIXEL = 7,
};
struct hdmi_core_vid_config {
struct hdmi_config v_fc_config;
enum hdmi_core_packet_mode packet_mode;
int data_enable_pol;
int vblank_osc;
int hblank;
int vblank;
};
struct csc_table {
u16 a1, a2, a3, a4;
u16 b1, b2, b3, b4;
u16 c1, c2, c3, c4;
};
int hdmi5_read_edid(struct hdmi_core_data *core, u8 *edid, int len);
void hdmi5_core_dump(struct hdmi_core_data *core, struct seq_file *s);
int hdmi5_core_handle_irqs(struct hdmi_core_data *core);
void hdmi5_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
struct hdmi_config *cfg);
int hdmi5_core_init(struct platform_device *pdev, struct hdmi_core_data *core);
#if defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
int hdmi5_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
struct omap_dss_audio *audio, u32 pclk);
#endif
#endif
......@@ -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);
/*
* Copyright (C) 2009 Nokia Corporation
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
*
* VENC panel driver
*
* 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/kernel.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/mutex.h>
#include <linux/module.h>
#include <video/omapdss.h>
#include "dss.h"
static struct {
struct mutex lock;
} venc_panel;
static ssize_t display_output_type_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct omap_dss_device *dssdev = to_dss_device(dev);
const char *ret;
switch (dssdev->phy.venc.type) {
case OMAP_DSS_VENC_TYPE_COMPOSITE:
ret = "composite";
break;
case OMAP_DSS_VENC_TYPE_SVIDEO:
ret = "svideo";
break;
default:
return -EINVAL;
}
return snprintf(buf, PAGE_SIZE, "%s\n", ret);
}
static ssize_t display_output_type_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct omap_dss_device *dssdev = to_dss_device(dev);
enum omap_dss_venc_type new_type;
if (sysfs_streq("composite", buf))
new_type = OMAP_DSS_VENC_TYPE_COMPOSITE;
else if (sysfs_streq("svideo", buf))
new_type = OMAP_DSS_VENC_TYPE_SVIDEO;
else
return -EINVAL;
mutex_lock(&venc_panel.lock);
if (dssdev->phy.venc.type != new_type) {
dssdev->phy.venc.type = new_type;
omapdss_venc_set_type(dssdev, new_type);
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
omapdss_venc_display_disable(dssdev);
omapdss_venc_display_enable(dssdev);
}
}
mutex_unlock(&venc_panel.lock);
return size;
}
static DEVICE_ATTR(output_type, S_IRUGO | S_IWUSR,
display_output_type_show, display_output_type_store);
static int venc_panel_probe(struct omap_dss_device *dssdev)
{
/* set default timings to PAL */
const struct omap_video_timings default_timings = {
.x_res = 720,
.y_res = 574,
.pixelclock = 13500000,
.hsw = 64,
.hfp = 12,
.hbp = 68,
.vsw = 5,
.vfp = 5,
.vbp = 41,
.vsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
.hsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
.interlace = true,
};
mutex_init(&venc_panel.lock);
dssdev->panel.timings = default_timings;
return device_create_file(dssdev->dev, &dev_attr_output_type);
}
static void venc_panel_remove(struct omap_dss_device *dssdev)
{
device_remove_file(dssdev->dev, &dev_attr_output_type);
}
static int venc_panel_enable(struct omap_dss_device *dssdev)
{
int r;
dev_dbg(dssdev->dev, "venc_panel_enable\n");
mutex_lock(&venc_panel.lock);
if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
r = -EINVAL;
goto err;
}
omapdss_venc_set_timings(dssdev, &dssdev->panel.timings);
omapdss_venc_set_type(dssdev, dssdev->phy.venc.type);
omapdss_venc_invert_vid_out_polarity(dssdev,
dssdev->phy.venc.invert_polarity);
r = omapdss_venc_display_enable(dssdev);
if (r)
goto err;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
mutex_unlock(&venc_panel.lock);
return 0;
err:
mutex_unlock(&venc_panel.lock);
return r;
}
static void venc_panel_disable(struct omap_dss_device *dssdev)
{
dev_dbg(dssdev->dev, "venc_panel_disable\n");
mutex_lock(&venc_panel.lock);
if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED)
goto end;
omapdss_venc_display_disable(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
end:
mutex_unlock(&venc_panel.lock);
}
static void venc_panel_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
dev_dbg(dssdev->dev, "venc_panel_set_timings\n");
mutex_lock(&venc_panel.lock);
omapdss_venc_set_timings(dssdev, timings);
dssdev->panel.timings = *timings;
mutex_unlock(&venc_panel.lock);
}
static int venc_panel_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
dev_dbg(dssdev->dev, "venc_panel_check_timings\n");
return omapdss_venc_check_timings(dssdev, timings);
}
static u32 venc_panel_get_wss(struct omap_dss_device *dssdev)
{
dev_dbg(dssdev->dev, "venc_panel_get_wss\n");
return omapdss_venc_get_wss(dssdev);
}
static int venc_panel_set_wss(struct omap_dss_device *dssdev, u32 wss)
{
dev_dbg(dssdev->dev, "venc_panel_set_wss\n");
return omapdss_venc_set_wss(dssdev, wss);
}
static struct omap_dss_driver venc_driver = {
.probe = venc_panel_probe,
.remove = venc_panel_remove,
.enable = venc_panel_enable,
.disable = venc_panel_disable,
.get_resolution = omapdss_default_get_resolution,
.get_recommended_bpp = omapdss_default_get_recommended_bpp,
.set_timings = venc_panel_set_timings,
.check_timings = venc_panel_check_timings,
.get_wss = venc_panel_get_wss,
.set_wss = venc_panel_set_wss,
.driver = {
.name = "venc",
.owner = THIS_MODULE,
},
};
int venc_panel_init(void)
{
return omap_dss_register_driver(&venc_driver);
}
void venc_panel_exit(void)
{
omap_dss_unregister_driver(&venc_driver);
}
......@@ -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