[PATCH] video: tegra: add tegra display controller and fb driver

Ryan Mallon ryan at bluewatersys.com
Thu Aug 12 00:34:41 EDT 2010


On 08/12/2010 11:11 AM, Erik Gilling wrote:
> This patch supersedes the previous framebuffer patch
> 
> Supports:
> 	* panel setup
> 	* overlays
> 	* suspend / resume
> 
> Notable ommisions:
> 	* support for anything but lvds panels
> 	* inegration with nvhost driver to sync updates with 3D
> 	* FB physical geometry is not set
> 	* lacks interface to set overlay/window x,y offset
> 
> Signed-off-by: Erik Gilling <konkers at android.com>
> Cc: Colin Cross <ccross at android.com>
> Cc: Travis Geiselbrecht <travis at palm.com>

Hi Erik,

Just a couple of notes, not really a full review.

> +++ b/drivers/video/tegra/Kconfig
> @@ -0,0 +1,15 @@
> +config TEGRA_DC
> +	tristate "Tegra Display Contoller"
> +	depends on ARCH_TEGRA
> +	help
> +	  Tegra display controller support.
> +
> +config FB_TEGRA
> +	tristate "Tegra Framebuffer driver"
> +	depends on TEGRA_DC && FB = y

How come this depends on FB=y?

> +++ b/drivers/video/tegra/dc/Makefile
> @@ -0,0 +1,2 @@
> +obj-y += dc.o
> +obj-y += rgb.o
> \ No newline at end of file

Is that meant to be there?

> +struct tegra_dc_blend tegra_dc_blend_modes[][DC_N_WINDOWS] = {
> +	{{.nokey = BLEND(NOKEY, FIX, 0xff, 0xff),
> +	  .one_win = BLEND(NOKEY, FIX, 0xff, 0xff),
> +	  .two_win_x = BLEND(NOKEY, FIX, 0x00, 0x00),
> +	  .two_win_y = BLEND(NOKEY, DEPENDANT, 0x00, 0x00),
> +	  .three_win_xy = BLEND(NOKEY, FIX, 0x00, 0x00)},
> +	 {.nokey = BLEND(NOKEY, FIX, 0xff, 0xff),
> +	  .one_win = BLEND(NOKEY, FIX, 0xff, 0xff),
> +	  .two_win_x = BLEND(NOKEY, FIX, 0xff, 0xff),
> +	  .two_win_y = BLEND(NOKEY, DEPENDANT, 0x00, 0x00),
> +	  .three_win_xy = BLEND(NOKEY, DEPENDANT, 0x00, 0x00)},
> +	 {.nokey = BLEND(NOKEY, FIX, 0xff, 0xff),
> +	  .one_win = BLEND(NOKEY, FIX, 0xff, 0xff),
> +	  .two_win_x = BLEND(NOKEY, ALPHA, 0xff, 0xff),
> +	  .two_win_y = BLEND(NOKEY, ALPHA, 0xff, 0xff),
> +	  .three_win_xy = BLEND(NOKEY, ALPHA, 0xff, 0xff)}
> +	}
> +};

Tab delimiting this would make it a bit more readable.

> +#define DUMP_REG(a) do {			\
> +	snprintf(buff, sizeof(buff), "%-32s\t%03x\t%08lx\n", \
> +		 #a, a, tegra_dc_readl(dc, a));		      \
> +	print(data, buff);				      \
> +	} while (0)
> +
> +static void _dump_regs(struct tegra_dc *dc, void *data,
> +		       void (* print)(void *data, const char *str))
> +{
> +	int i;
> +	char buff[256];
> +
> +	DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT);
> +	DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
> +	DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT_ERROR);

<snip>

The dump_regs code is very long for a debugging feature. Can it just be
replaced by a for loop which prints the offsets and values of each register?
> +
> +#undef DUMP_REG
> +
> +#ifdef DEBUG
> +static void dump_regs_print(void *data, const char *str)
> +{
> +	struct tegra_dc *dc = data;
> +	dev_dbg(&dc->pdev->dev, "%s", str);
> +}
> +
> +static void dump_regs(struct tegra_dc *dc)
> +{
> +	_dump_regs(dc, dc, dump_regs_print);
> +}
> +#else
> +
> +static void dump_regs(struct tegra_dc *dc) {}
> +
> +#endif
> +
> +#ifdef CONFIG_DEBUG_FS
> +
> +static void dbg_regs_print(void *data, const char *str)
> +{
> +	struct seq_file *s = data;
> +
> +	seq_printf(s, "%s", str);
> +}
> +
> +#undef DUMP_REG
> +
> +static int dbg_dc_show(struct seq_file *s, void *unused)
> +{
> +	struct tegra_dc *dc = s->private;
> +
> +	_dump_regs(dc, s, dbg_regs_print);
> +
> +	return 0;
> +}

This all looks a bit confusing (especially the undef stuff). Why do you
have both a debugfs interface to the registers and one which prints them
using dev_dbg?

> +static void tegra_dc_dbg_add(struct tegra_dc *dc)
> +{
> +	char name[32];
> +
> +	snprintf(name, sizeof(name), "tegra_dc%d_regs", dc->pdev->id);
> +	(void) debugfs_create_file(name, S_IRUGO, NULL, dc, &dbg_fops);

Don't cast the return value to void. Possibly print a warning if the
call fails?

> +/* does not support syncing windows on multiple dcs in one call */
> +int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n)
> +{
> +	if (n < 1 || n > DC_N_WINDOWS)
> +		return -EINVAL;

 if (n < 1 || n >= DC_N_WINDOWS) ?

> +	return wait_event_interruptible_timeout(windows[0]->dc->wq,
> +					 tegra_dc_windows_are_clean(windows, n),
> +					 HZ);
> +}
> +EXPORT_SYMBOL(tegra_dc_sync_windows);

<snip>

> +static irqreturn_t tegra_dc_irq(int irq, void *ptr)
> +{
> +	struct tegra_dc *dc = ptr;
> +	unsigned long status;
> +	unsigned long flags;
> +	unsigned long val;
> +	int i;
> +
> +

There are a few places like this with excess blank lines.

> +	tegra_dc_init(dc);
> +
> +	tegra_dc_set_blending(dc, tegra_dc_blend_modes[0]);
> +
> +	platform_set_drvdata(pdev, dc);
> +
> +	tegra_dc_dbg_add(dc);
> +
> +	dev_info(&pdev->dev, "probed\n");

dev_dbg? Also, probably don't need all those blank lines.

> +#ifdef CONFIG_PM
> +static int tegra_dc_suspend(struct platform_device *pdev, pm_message_t state)
> +{
> +	struct tegra_dc *dc = platform_get_drvdata(pdev);
> +
> +	dev_info(&pdev->dev, "suspend\n");

dev_dbg?

> +	disable_irq(dc->irq);
> +	tegra_periph_reset_assert(dc->clk);
> +	clk_disable(dc->clk);
> +
> +	return 0;
> +}
> +
> +static int tegra_dc_resume(struct platform_device *pdev)
> +{
> +	struct tegra_dc *dc = platform_get_drvdata(pdev);
> +	struct tegra_dc_win *wins[DC_N_WINDOWS];
> +	int i;
> +
> +	dev_info(&pdev->dev, "resume\n");

dev_dbg?

> +extern int suspend_get(char *buffer, struct kernel_param *kp)
> +{
> +	return 0;
> +}
> +
> +int suspend;

Should be static? Have a global called suspend is likely to cause issues
somewhere.

> +module_param_call(suspend, suspend_set, suspend_get, &suspend, 0644);
> +
> +struct platform_driver tegra_dc_driver = {
> +	.driver = {
> +		.name = "tegradc",
> +		.owner = THIS_MODULE,
> +	},
> +	.probe = tegra_dc_probe,
> +	.remove = tegra_dc_remove,
> +#ifdef CONFIG_PM
> +	.suspend = tegra_dc_suspend,
> +	.resume = tegra_dc_resume,
> +#endif
> +};

Also should be static?

> +static inline unsigned long tegra_dc_readl(struct tegra_dc *dc,
> +					   unsigned long reg)
> +{
> +	return readl(dc->base + reg * 4);
> +}

Can these functions use __raw_readl/__raw_writel?

> +static void tegra_fb_fillrect(struct fb_info *info,
> +			      const struct fb_fillrect *rect)
> +{
> +	cfb_fillrect(info, rect);
> +}
> +
> +static void tegra_fb_copyarea(struct fb_info *info,
> +			      const struct fb_copyarea *region)
> +{
> +	cfb_copyarea(info, region);
> +}
> +
> +static void tegra_fb_imageblit(struct fb_info *info,
> +			       const struct fb_image *image)
> +{
> +	cfb_imageblit(info, image);
> +}

You can just set the cfb_ functions directly in the fb_ops. See below:

> +static struct fb_ops tegra_fb_ops = {
> +	.owner = THIS_MODULE,
> +	.fb_open = tegra_fb_open,
> +	.fb_release = tegra_fb_release,
> +	.fb_check_var = tegra_fb_check_var,
> +	.fb_set_par = tegra_fb_set_par,
> +	.fb_setcolreg = tegra_fb_setcolreg,
> +	.fb_pan_display = tegra_fb_pan_display,
> +	.fb_fillrect = tegra_fb_fillrect,
> +	.fb_copyarea = tegra_fb_copyarea,
> +	.fb_imageblit = tegra_fb_imageblit,

Should be:

	.fb_fillrect  = cfb_fillrect,
	.fb_copyarea  = cfb_fillarea,
	.fb_imageblit = cfb_imageblit,

Also (nitpicky) tab delimit to make it more readable.

~Ryan

-- 
Bluewater Systems Ltd - ARM Technology Solution Centre

Ryan Mallon         		5 Amuri Park, 404 Barbadoes St
ryan at bluewatersys.com         	PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com	New Zealand
Phone: +64 3 3779127		Freecall: Australia 1800 148 751
Fax:   +64 3 3779135			  USA 1800 261 2934



More information about the linux-arm-kernel mailing list