Re: [PATCH v3 02/16] drm: sti: add VTAC drivers
From: Thierry Reding
Date: Wed May 21 2014 - 11:30:38 EST
First off: a lot of the general style comments from my review of patch
01/16 also apply here.
On Tue, May 20, 2014 at 03:56:12PM +0200, Benjamin Gaignard wrote:
> Video Trafic Advance Communication Rx and Tx drivers are designed
s/Trafic/Traffic/?
> diff --git a/drivers/gpu/drm/sti/Kconfig b/drivers/gpu/drm/sti/Kconfig
[...]
> @@ -4,6 +4,12 @@ config DRM_STI
> help
> Choose this option to enable DRM on STM stiH41x chipset
>
> +config VTAC_STI
> + bool "Video Trafic Advance Communication Rx and Tx for STMicroelectronics SoC stiH41x Series"
Here too.
> + depends on DRM_STI
> + help
> + Choose this option to enable VTAC on STM stiH41x chipset
I wonder how useful it is to make all of these user-selectable (or even
Kconfig symbols to begin with). It seems like VTG and VTAC (and the same
is likely true for other subdrivers) are essential to make this driver
do anything useful, so in my opinion these should all be built
unconditionally.
> diff --git a/drivers/gpu/drm/sti/sti_vtac_rx.c b/drivers/gpu/drm/sti/sti_vtac_rx.c
[...]
> @@ -0,0 +1,169 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2013
Nit: this probably needs an update to include 2014.
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
Should be sorted alphabetically.
> +#include <drm/drmP.h>
> +
> +#include "sti_vtac_utils.h"
> +
> +#define VTAC_RX_CONFIG 0x00
> +#define VTAC_RX_FIFO_CONFIG 0x04
> +
> +#define VTAC_SW_RST_AUTOC 0x02
> +#define VTAC_FIFO_CONFIG_VAL 0x04
> +
> +/*
> + * VTAC RX structure
> + *
> + * @dev: pointer to device structure
> + * @regs: ioremapped regsiters
s/regsiters/registers/
> + * @clk: clock
> + * @type: main or aux device
> + */
Also it's typically not necessary to document these driver-internal data
structures. The meaning of the fields is usually obvious. Also these
aren't usually exported to any documentation (because driver-internal)
and hence kernel-doc isn't such a good fit.
> +struct sti_vtac_rx {
> + struct device *dev;
> + void __iomem *regs;
> + struct clk *clk;
> + int type;
> +};
> +
> +static void sti_vtac_rx_reg_dump(struct sti_vtac_rx *vtac_rx)
> +{
> + dev_dbg(vtac_rx->dev, "vtac_rx->regs %p\n", vtac_rx->regs);
> + dev_dbg(vtac_rx->dev, "VTAC_RX_CONFIG 0x%x\n",
> + readl(vtac_rx->regs + VTAC_RX_CONFIG));
> + dev_dbg(vtac_rx->dev, "VTAC_RX_FIFO_CONFIG 0x%x\n",
> + readl(vtac_rx->regs + VTAC_RX_FIFO_CONFIG));
> +}
Similar as in the previous patch, this would be desirable to have in
debugfs.
> +static void sti_vtac_rx_set_config(struct sti_vtac_rx *vtac_rx)
> +{
> + int i;
> + u32 rx_config = EVEN_PARITY | ODD_PARITY | SW_RST_AUTOC | ENABLE;
For readability I think it would make sense to move this closer to where
the register is written.
> +
> + /* Enable VTAC clock */
> + if (clk_prepare_enable(vtac_rx->clk))
> + DRM_ERROR("Failed to prepare/enable vtac_rx clock.\n");
> +
> + for (i = 0; i < ARRAY_SIZE(vtac_modes); i++) {
> + if (vtac_modes[i].type == vtac_rx->type) {
Rather than try to lookup the mode by type in a global table, could you
not simply pass in the specific mode that should be configured as a
parameter. That way you can probably leave out the type field from the
sti_vtac_rx structure and use this in a similar way than what I
suggested from the
> + writel(VTAC_FIFO_CONFIG_VAL,
> + vtac_rx->regs + VTAC_RX_FIFO_CONFIG);
> + rx_config |= vtac_modes[i].vid_in_width << 4;
> + rx_config |= vtac_modes[i].phyts_width << 16;
> + rx_config |= vtac_modes[i].phyts_per_pixel << 23;
> + rx_config |= VTAC_SW_RST_AUTOC;
> + writel(rx_config, vtac_rx->regs + VTAC_RX_CONFIG);
> + }
> + }
> +}
> +
> +static int vtac_rx_bind(struct device *dev, struct device *master, void *data)
> +{
> + return 0;
> +}
> +
> +static void vtac_rx_unbind(struct device *dev, struct device *master,
> + void *data)
> +{
> + /* do nothing */
> +}
> +
> +static const struct component_ops vtac_rx_ops = {
> + .bind = vtac_rx_bind,
> + .unbind = vtac_rx_unbind,
> +};
> +
> +static int sti_vtac_rx_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct device_node *np = dev->of_node;
> + struct sti_vtac_rx *vtac_rx;
> + struct resource *res;
> +
> + DRM_INFO("%s\n", __func__);
> +
> + vtac_rx = devm_kzalloc(dev, sizeof(*vtac_rx), GFP_KERNEL);
> + if (!vtac_rx) {
> + DRM_ERROR("Failed to allocate VTAC RX context\n");
> + return -ENOMEM;
> + }
> + vtac_rx->dev = dev;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + DRM_ERROR("Invalid resource\n");
No need for this. devm_ioremap_resource() already checks for that and
outputs an error message.
> + return -ENOMEM;
> + }
> + vtac_rx->regs = devm_ioremap_resource(dev, res);
> + if (IS_ERR(vtac_rx->regs))
> + return PTR_ERR(vtac_rx->regs);
> +
> + vtac_rx->type = VTAC_MAIN;
> + if (np)
> + if (of_property_read_bool(np, "vtac-rx-aux"))
> + vtac_rx->type = VTAC_AUX;
What exactly is the difference between auxiliary and main here? It seems
like the only reason why you separate things here is because you use
different names for the clocks and devices.
> + if (vtac_rx->type == VTAC_MAIN) {
> + vtac_rx->clk = devm_clk_get(dev, "vtac_main_phy");
> + if (IS_ERR(vtac_rx->clk)) {
> + DRM_ERROR("Cannot get vtac_main_phy clock\n");
> + return PTR_ERR(vtac_rx->clk);
> + }
> + } else {
> + vtac_rx->clk = devm_clk_get(dev, "vtac_aux_phy");
> + if (IS_ERR(vtac_rx->clk)) {
> + DRM_ERROR("Cannot get vtac_aux_phy clock\n");
> + return PTR_ERR(vtac_rx->clk);
> + }
> + }
There should be no need to make the consumer name of the clock different
for main or auxiliary types. From the above it seems like the clock is
used to drive a PHY, in which case something like "phy" would be good
enough. That way you don't have to conditionalize.
> + sti_vtac_rx_set_config(vtac_rx);
> + sti_vtac_rx_reg_dump(vtac_rx);
> +
> + platform_set_drvdata(pdev, vtac_rx);
> + DRM_INFO("%s VTAC RX %s\n", __func__,
> + vtac_rx->type == VTAC_MAIN ? "main" : "aux");
> +
> + return component_add(&pdev->dev, &vtac_rx_ops);
> +}
> +
> +static int sti_vtac_rx_remove(struct platform_device *pdev)
> +{
> + component_del(&pdev->dev, &vtac_rx_ops);
> + return 0;
> +}
> +
> +static struct of_device_id vtac_rx_match_types[] = {
Should be static const.
> + {
> + .compatible = "st,stih416-vtac-rx",
> + }, {
> + /* end node */
> + }
Funky indentation.
> +};
> +
> +MODULE_DEVICE_TABLE(of, vtac_rx_match_types);
I think it's more common to not have a blank line between the }; and the
MODULE_DEVICE_TABLE. I missed that for 01/12.
> +
> +struct platform_driver sti_vtac_rx_driver = {
> + .driver = {
> + .name = "sti-vtac-rx",
> + .owner = THIS_MODULE,
> + .of_match_table = vtac_rx_match_types,
> + },
> + .probe = sti_vtac_rx_probe,
> + .remove = sti_vtac_rx_remove,
> +};
> +
> +module_platform_driver(sti_vtac_rx_driver);
Same here, and that also applies to 01/12.
> +
> +MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@xxxxxx>");
> +MODULE_DESCRIPTION("STMicroelectronics SoC VTAC_RX driver");
> +MODULE_LICENSE("GPL");
This doesn't match the license in the file header. This is also wrong in
patch 01/12.
> diff --git a/drivers/gpu/drm/sti/sti_vtac_tx.c b/drivers/gpu/drm/sti/sti_vtac_tx.c
> new file mode 100644
> index 0000000..3c0a152
> --- /dev/null
> +++ b/drivers/gpu/drm/sti/sti_vtac_tx.c
> @@ -0,0 +1,182 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2013
> + * Author: Benjamin Gaignard <benjamin.gaignard@xxxxxx> for STMicroelectronics.
> + * License terms: GNU General Public License (GPL), version 2
> + */
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <drm/drmP.h>
> +
> +#include "sti_vtac_utils.h"
> +
> +#define VTAC_TX_CONFIG 0x00
> +
> +#define VTAC_SW_RST_AUTOC 0x02
> +
> +#define VTAC_TX_PHY_ENABLE_CLK_PHY (0x01 << 0)
> +#define VTAC_TX_PHY_PROG_N3 (0x04 << 7)
> +#define VTAC_TX_PHY_ENABLE_CLK_DLL (0x01 << 1)
> +#define VTAC_TX_PHY_RST_N_DLL_SWITCH (0x01 << 4)
> +#define VTAC_TX_PHY_PLL_NOT_OSC_MODE (0x01 << 3)
> +
> +/*
> + * VTAC TX structure
> + *
> + * @dev: pointer to device structure
> + * @regs: ioremapped regsiters
> + * @clk: clock
> + * @type: main or aux device
> + */
> +struct sti_vtac_tx {
> + struct device *dev;
> + void __iomem *tx_regs;
> + void __iomem *phy_regs;
If these are separate register regions, perhaps the PHY part should be
exposed using the generic PHY framework (see Documentation/phy.txt).
> +static void sti_vtac_tx_set_config(struct sti_vtac_tx *vtac_tx)
> +{
> + int i;
> + u32 phy_config;
> + u32 tx_config = EVEN_PARITY | ODD_PARITY | SW_RST_AUTOC | ENABLE;
Both of these are awfully long names. Perhaps you can use a single
variable called value here.
> +
> + /* Enable VTAC clock */
> + if (clk_prepare_enable(vtac_tx->clk))
> + DRM_ERROR("Failed to prepare/enable vtac_tx clock.\n");
This would typically be a fatal error, wouldn't it? So this should
return (and probably return the error code as well).
> +
> + /* Configure vtac phy */
> + phy_config = 0x00000000;
> + writel(phy_config, vtac_tx->phy_regs + 0x828); /* SYS_CFG8522 */
> + phy_config = VTAC_TX_PHY_ENABLE_CLK_PHY;
> + writel(phy_config, vtac_tx->phy_regs + 0x824); /* SYS_CFG8521 */
> + phy_config = readl(vtac_tx->phy_regs + 0x824);
> + phy_config |= VTAC_TX_PHY_PROG_N3;
> + writel(phy_config, vtac_tx->phy_regs + 0x824); /* SYS_CFG8521 */
> + phy_config = readl(vtac_tx->phy_regs + 0x824);
> + phy_config |= VTAC_TX_PHY_ENABLE_CLK_DLL;
> + writel(phy_config, vtac_tx->phy_regs + 0x824); /* SYS_CFG8521 */
> + phy_config = readl(vtac_tx->phy_regs + 0x824);
> + phy_config |= VTAC_TX_PHY_RST_N_DLL_SWITCH;
> + writel(phy_config, vtac_tx->phy_regs + 0x824); /* SYS_CFG8521 */
> + phy_config = readl(vtac_tx->phy_regs + 0x824);
> + phy_config |= VTAC_TX_PHY_PLL_NOT_OSC_MODE;
> + writel(phy_config, vtac_tx->phy_regs + 0x824); /* SYS_CFG8521 */
Can we have a symbolic name for registers 0x824 and 0x828 here, please?
> +
> + /* Configure vtac tx */
> + for (i = 0; i < ARRAY_SIZE(vtac_modes); i++) {
> + if (vtac_modes[i].type == vtac_tx->type) {
> + tx_config |= vtac_modes[i].vid_in_width << 4;
> + tx_config |= vtac_modes[i].phyts_width << 16;
> + tx_config |= vtac_modes[i].phyts_per_pixel << 23;
> + tx_config |= VTAC_SW_RST_AUTOC;
You already set SW_RST_AUTOC in tx_config and it seems to be the same
value as VTAC_SW_RST_AUTOC. Also please provide only one name for a
given field, either VTAC_SW_RST_AUTOC *or* SW_RST_AUTOC, not both.
> + writel(tx_config, vtac_tx->tx_regs + VTAC_TX_CONFIG);
Similarly to VTAC Rx I'd set the variable right before writing it to the
register here to make it immediately obvious what value gets written.
Also this is the exact same programming sequence as for VTAC Rx, and the
config register seems to be at the same offset as well, so I'm wondering
if perhaps Rx and Tx VTACs are actually the same hardware. If so you can
probably get away with deduplicating both drivers and parameterizing. If
you move out the PHY register programming into a PHY driver, then most
of that work is already done.
> +static int sti_vtac_tx_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct device_node *np = dev->of_node;
> + struct sti_vtac_tx *vtac_tx;
> + struct resource *res;
> +
> + DRM_INFO("%s\n", __func__);
> +
> + vtac_tx = devm_kzalloc(dev, sizeof(*vtac_tx), GFP_KERNEL);
> + if (!vtac_tx) {
> + DRM_ERROR("Failed to allocate VTAC TX context\n");
> + return -ENOMEM;
> + }
> + vtac_tx->dev = dev;
> +
> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vtac-tx");
> + if (!res) {
> + DRM_ERROR("Invalid resource 'vtac-tx'\n");
> + return -ENOMEM;
> + }
> + vtac_tx->tx_regs = devm_ioremap_resource(dev, res);
> + if (IS_ERR(vtac_tx->tx_regs))
> + return PTR_ERR(vtac_tx->tx_regs);
> +
> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vtac-phy");
> + if (!res) {
> + DRM_ERROR("Invalid resource 'vtac-phy'\n");
> + return -ENOMEM;
> + }
> + vtac_tx->phy_regs = devm_ioremap_nocache(dev, res->start,
> + resource_size(res));
> + if (IS_ERR(vtac_tx->phy_regs))
> + return PTR_ERR(vtac_tx->phy_regs);
Why is this not using devm_ioremap_resource() like the tx_regs resource?
Also if you move this to a separate PHY driver your resource management
becomes a whole lot simpler (no need to worry about the tx_ or phy_
prefixes, ...).
> +static struct of_device_id vtac_tx_match_types[] = {
vtac_tx_of_match? Or if you can unify both drivers: vtac_of_match. If
some parameterization is required to make Tx and Rx work in one driver
you could add different compatible strings here for each. More likely
though I think that information could be derived from context.
> + {
> + .compatible = "st,stih416-vtac-tx",
> + }, {
> + /* end node */
> + }
> +};
Strange indentation here.
> +MODULE_DEVICE_TABLE(of, vtac_tx_match_types);
> +
> +struct platform_driver sti_vtac_tx_driver = {
> + .driver = {
> + .name = "sti-vtac-tx",
> + .owner = THIS_MODULE,
> + .of_match_table = vtac_tx_match_types,
> + },
And here.
> diff --git a/drivers/gpu/drm/sti/sti_vtac_utils.h b/drivers/gpu/drm/sti/sti_vtac_utils.h
[...]
> +/* Number of phyts per pixel */
What's a "phyt"?
> +#define VTAC_2_5_PPP 0x0005
> +#define VTAC_3_PPP 0x0006
> +#define VTAC_4_PPP 0x0008
> +#define VTAC_5_PPP 0x000A
> +#define VTAC_6_PPP 0x000C
> +#define VTAC_13_PPP 0x001A
> +#define VTAC_14_PPP 0x001C
> +#define VTAC_15_PPP 0x001E
> +#define VTAC_16_PPP 0x0020
> +#define VTAC_17_PPP 0x0022
> +#define VTAC_18_PPP 0x0024
> +
> +/* enable bits */
> +#define EVEN_PARITY (1 << 13)
> +#define ODD_PARITY (1 << 12)
> +#define SW_RST_AUTOC (1 << 1)
> +#define ENABLE (1 << 0)
These could probably use a VTAC_ prefix for consistency.
> +
> +/*
> + * VTAC mode structure
> + *
> + * @type: main, aux or dvo device
> + * @vid_in_width: Video Data Resolution
These are probably obvious if you know the hardware or can look at the
datasheet, but I have no idea what this field for example means. I had
initially thought that it would contain some number of pixels, but the
table of modes below taught me better. Having some explanation of the
fields in this structure could be useful.
> + * @phyts_width: Width of phyt buses(phyt low and phyt high).
> + * @phyts_per_pixel: Number of phyts sent per pixel
> + */
> +struct sti_vtac_mode {
> + int type;
> + int vid_in_width;
> + int phyts_width;
> + int phyts_per_pixel;
> +};
> +
> +static struct sti_vtac_mode vtac_modes[] = {
> + {VTAC_MAIN, 0x2, 0x2, VTAC_5_PPP}, /* Main RGB 12 */
> + {VTAC_AUX, 0x1, 0x0, VTAC_17_PPP}, /* Aux 10 */
> +};
This doesn't really belong in a header. If you manage to unify both VTAC
Tx and Rx you can simply move this into the VTAC driver source file and
not expose it beyond that at all.
Thierry
Attachment:
pgp2p4y_Goi29.pgp
Description: PGP signature