Zero-Start Driver Development, linux Driver (26, Samsung Frameebuffer)

Keywords: Mobile

The framebuffer device of Samsung platform is built based on platform bus.

It is divided into two parts:

Part one is the general operating interface and configuration interface for all processors of Samsung, which is called driver in platform bus.

The other part is the LCD-related time series, resolution and other easily variable parameters, called device in platform bus.

 

We will first analyze the LCD-related parameters that may vary for different hardware, of course, these parameters are in our section fb_info.

First, let's look at some time series related parameters:

static struct s3c_fb_pd_win smdkv210_fb_win0 = {
	.max_bpp	= 32,            /* Maximum support for bpp */
	.default_bpp	= 24,        /* The default number of bits per pixel */
	.xres		= 800,           /* Lateral resolution */
	.yres		= 480,           /* Longitudinal resolution */
};

static struct fb_videomode smdkv210_lcd_timing = {
	.left_margin	= 13,        /* Six timing parameters, the latter section will be transplanted to my board, and then analysis */
	.right_margin	= 8,
	.upper_margin	= 7,
	.lower_margin	= 5,
	.hsync_len	= 3,
	.vsync_len	= 1,
	.xres		= 800,            /* Lateral resolution */
	.yres		= 480,            /* Longitudinal resolution */
};


static struct s3c_fb_platdata smdkv210_lcd0_pdata __initdata = {
	.win[0]		= &smdkv210_fb_win0,
	.vtiming	= &smdkv210_lcd_timing,
	.vidcon0	= VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,   /* Samsung currently supports RGB and I80 for control panel data format */
	.vidcon1	= VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,      /*  Used to control panel data output, that is to control whether HSYNC and VSYNC should be flipped */
	.setup_gpio	= s5pv210_fb_gpio_setup_24bpp,        /* Used to initialize LCD-related gpio mode */
};


void s5pv210_fb_gpio_setup_24bpp(void)
{
    /* Set up 28 registers related to LCD, 24 BPP data line + 4 clock or clock polarity related */
	s5pv210_fb_cfg_gpios(S5PV210_GPF0(0), 8);
	s5pv210_fb_cfg_gpios(S5PV210_GPF1(0), 8);
	s5pv210_fb_cfg_gpios(S5PV210_GPF2(0), 8);
	s5pv210_fb_cfg_gpios(S5PV210_GPF3(0), 4);

	/* Set DISPLAY_CONTROL register for Display path selection.
	 *
	 * ouput   |   RGB   |   I80   |   ITU
	 * -----------------------------------
	 *  00     |   MIE   |  FIMD   |  FIMD
	 *  01     | MDNIE   | MDNIE   |  FIMD
	 *  10     |  FIMD   |  FIMD   |  FIMD
	 *  11     |  FIMD   |  FIMD   |  FIMD
	 */
	writel(0x2, S5P_MDNIE_SEL);    /* display mode */
}


The above parameters are set to the data of the platform equipment through the following functions

static void __init smdkv210_machine_init(void)
{
	......
	s3c_fb_set_platdata(&smdkv210_lcd0_pdata);
    ......
}

/* The purpose of this function is to put our parameters into the data of the platform data device. */
void __init s3c_fb_set_platdata(struct s3c_fb_platdata *pd)
{
	s3c_set_platdata(pd, sizeof(struct s3c_fb_platdata),
			 &s3c_device_fb);
}

/* Platform equipment */
struct platform_device s3c_device_fb = {
	.name		= "s3c-fb",        /* Platform devices need driver name consistency to match */
	.id		= -1,
	.num_resources	= ARRAY_SIZE(s3c_fb_resource),    /* Resource size */
	.resource	= s3c_fb_resource,                    /* Resource type */
	.dev		= {
		.dma_mask		= &samsung_device_dma_mask,    /* dma equipment */
		.coherent_dma_mask	= DMA_BIT_MASK(32),
	},
};


/* Put the smdkv210_lcd0_pdata above us into the data of the platform device */
void __init *s3c_set_platdata(void *pd, size_t pdsize,
			      struct platform_device *pdev)
{
	void *npd;

	if (!pd) {        /* Effective judgement */
		/* too early to use dev_name(), may not be registered */
		printk(KERN_ERR "%s: no platform data supplied\n", pdev->name);
		return NULL;
	}

	npd = kmemdup(pd, pdsize, GFP_KERNEL);    /* Dynamic application space, copy a parameter passed in above */
	if (!npd) {
		printk(KERN_ERR "%s: cannot clone platform data\n", pdev->name);
		return NULL;
	}

	pdev->dev.platform_data = npd;        /* Binding to Platform Device Data */
	return npd;
}

/* Look at the resources associated with lcd */
static struct resource s3c_fb_resource[] = {
	[0] = DEFINE_RES_MEM(S3C_PA_FB, SZ_16K),    /* Control registers, etc. */
	[1] = DEFINE_RES_IRQ(IRQ_LCD_VSYNC),        /* interrupt */
	[2] = DEFINE_RES_IRQ(IRQ_LCD_FIFO),         /* interrupt */
	[3] = DEFINE_RES_IRQ(IRQ_LCD_SYSTEM),       /* interrupt */
};


As for LCD, it is also important to note that LCD needs backlight to display, so we also need to look at the interface function of backlight to facilitate later transplantation.

static struct platform_device smdkv210_lcd_lte480wv = {
	.name			= "platform-lcd",        /* Backlight name */
	.dev.parent		= &s3c_device_fb.dev,    /* Depend on lcd */
	.dev.platform_data	= &smdkv210_lcd_lte480wv_data,    /* Backlight-related data interface */
};


static struct plat_lcd_data smdkv210_lcd_lte480wv_data = {
	.set_power	= smdkv210_lte480wv_set_power,
};

static void smdkv210_lte480wv_set_power(struct plat_lcd_data *pd,
					unsigned int power)
{
	if (power) {        /* power To be true, to turn on the backlight */
#If! Defined (CONFIG_BACKLIGHT_PWM)/* If the PWM dimming function is turned on, the PWM */
		gpio_request_one(S5PV210_GPD0(3), GPIOF_OUT_INIT_HIGH, "GPD0");
		gpio_free(S5PV210_GPD0(3));
#endif

		/* fire nRESET on power up Set Backlight Port to High Level  */
		gpio_request_one(S5PV210_GPH0(6), GPIOF_OUT_INIT_HIGH, "GPH0");

		gpio_set_value(S5PV210_GPH0(6), 0);
		mdelay(10);

		gpio_set_value(S5PV210_GPH0(6), 1);
		mdelay(10);

		gpio_free(S5PV210_GPH0(6));
	} else {
#if !defined(CONFIG_BACKLIGHT_PWM)
		gpio_request_one(S5PV210_GPD0(3), GPIOF_OUT_INIT_LOW, "GPD0");
		gpio_free(S5PV210_GPD0(3));
#endif
	}
}

In the end, it depends on how the platform equipment is registered.

/* lcd Platform data and backlight are added to a unified array */
static struct platform_device *smdkv210_devices[] __initdata = {
    ......
	&s3c_device_fb,        
    ......
	&smdkv210_lcd_lte480wv,
};


static void __init smdkv210_machine_init(void)
{
    ......
	
	s3c_fb_set_platdata(&smdkv210_lcd0_pdata);

    .....
    /* Unified registration of all platform devices here */
	platform_add_devices(smdkv210_devices, ARRAY_SIZE(smdkv210_devices));

    .....
}

Here we need to note that Samsung re-assigns the name of some platform data, including our flat fb device, in order to reuse the code of all platforms as much as possible.

MACHINE_START(SMDKV210, "SMDKV210")
	/* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */
	.atag_offset	= 0x100,
	.init_irq	= s5pv210_init_irq,
	.map_io		= smdkv210_map_io,    /* When this function is set, it is executed at the start-up stage. */
	.init_machine	= smdkv210_machine_init,
	.init_time	= samsung_timer_init,
	.restart	= s5pv210_restart,
	.reserve	= &smdkv210_reserve,
MACHINE_END


static void __init smdkv210_map_io(void)
{
	s5pv210_init_io(NULL, 0);        /* Set up here */
	s3c24xx_init_clocks(clk_xusbxti.rate);
	s3c24xx_init_uarts(smdkv210_uartcfgs, ARRAY_SIZE(smdkv210_uartcfgs));
	samsung_set_timer_source(SAMSUNG_PWM2, SAMSUNG_PWM4);
}

void __init s5pv210_init_io(struct map_desc *mach_desc, int size)
{
	/* initialize the io descriptors we need for initialization */
	iotable_init(s5pv210_iodesc, ARRAY_SIZE(s5pv210_iodesc));
	if (mach_desc)
		iotable_init(mach_desc, size);

	/* detect cpu id and rev. */
	s5p_init_cpu(S5P_VA_CHIPID);

    /* The name of fb set here */
	s3c_init_cpu(samsung_cpu_id, cpu_ids, ARRAY_SIZE(cpu_ids));


	samsung_pwm_set_platdata(&s5pv210_pwm_variant);
}



static struct cpu_table cpu_ids[] __initdata = {
	{
		.idcode		= S5PV210_CPU_ID,
		.idmask		= S5PV210_CPU_MASK,
		.map_io		= s5pv210_map_io,        /* Call this function */
		.init_clocks	= s5pv210_init_clocks,
		.init_uarts	= s5pv210_init_uarts,
		.init		= s5pv210_init,
		.name		= name_s5pv210,
	},
};

void __init s3c_init_cpu(unsigned long idcode,
			 struct cpu_table *cputab, unsigned int cputab_size)
{
	cpu = s3c_lookup_cpu(idcode, cputab, cputab_size);

	if (cpu == NULL) {
		printk(KERN_ERR "Unknown CPU type 0x%08lx\n", idcode);
		panic("Unknown S3C24XX CPU");
	}

	printk("CPU %s (id 0x%08lx)\n", cpu->name, idcode);

	if (cpu->init == NULL) {
		printk(KERN_ERR "CPU %s support not enabled\n", cpu->name);
		panic("Unsupported Samsung CPU");
	}

	if (cpu->map_io)
		cpu->map_io();    /* Calling function */
}



void __init s5pv210_map_io(void)
{
	/* initialise device information early */
	s5pv210_default_sdhci0();
	s5pv210_default_sdhci1();
	s5pv210_default_sdhci2();
	s5pv210_default_sdhci3();

	s3c_adc_setname("samsung-adc-v3");

	s3c_cfcon_setname("s5pv210-pata");

	s3c_fimc_setname(0, "s5pv210-fimc");
	s3c_fimc_setname(1, "s5pv210-fimc");
	s3c_fimc_setname(2, "s5pv210-fimc");

	/* the i2c devices are directly compatible with s3c2440 */
	s3c_i2c0_setname("s3c2440-i2c");
	s3c_i2c1_setname("s3c2440-i2c");
	s3c_i2c2_setname("s3c2440-i2c");

	s3c_fb_setname("s5pv210-fb");        /* The name before overwriting is s5pv210-fb */

	/* Use s5pv210-keypad instead of samsung-keypad */
	samsung_keypad_setname("s5pv210-keypad");

	/* setup TV devices */
	s5p_hdmi_setname("s5pv210-hdmi");

	s3c64xx_spi_setname("s5pv210-spi");
}



/* Re-define device name depending on support. */
static inline void s3c_fb_setname(char *name)
{
#ifdef CONFIG_S3C_DEV_FB
	s3c_device_fb.name = name;
#endif
}

 

Next, we analyze the driver-related interface.

.

Posted by jonah on Mon, 28 Jan 2019 12:51:14 -0800