Backlight subsystem under Linux (i) "Turn"

Source: Internet
Author: User
Tags what interface

Transferred from: http://blog.csdn.net/weiqing1981127/article/details/8511676

All rights reserved, reprint must explain transfer from http://my.csdn.net/weiqing1981127

The original Nanjing University of Posts and telecommunications communication and information System Professional research two Wei Qing

A Overview of backlight back photon system

Our LCD screen often needs a backlight, adjust the brightness of the LCD screen backlight, where the backlight is not just light and not light two, but according to the needs of users, backlight brightness can be arbitrarily adjusted. Linux kernel has a backlight back photon system, the system is designed to meet the needs of users, as long as the user according to their own LCD backlight circuit PWM output pin, the kernel backlight subsystem code corresponding configuration, you can realize the LCD backlight.

LCD backlight principle is mainly by the core board of a pin control backlight power supply, a PWM pin control backlight composition, the application can change the frequency of PWM to change the backlight brightness.

Here we mainly explain the buzzer driver based on the backlight subsystem, in fact, simple to make the driver of the honey generator is very simple, here is just a buzzer as a device, and the principle of this device is similar to the backlight principle, are based on PWM, And our ultimate goal is to use the backlight back photon system. In summary, the backlight subsystem is a driver interface based on the PWM core, if you use a device is also based on PWM, and requires the user can adjust the PWM frequency to achieve such as changing the backlight brightness, change the buzzer frequency effect, Then you can use this backlight back photon system.

Two PWM Core Driver

Let's start with the PWM core.

First familiarize yourself with the following PWM core code in/ARCH/ARM/PLAT-S3C/PWM.C

View/arch/arm/plat-s3c/makefile

obj-$ (CONFIG_HAVE_PWM) + = PWM.O

View/arch/arm/plat-s3c/konfig and find no corresponding HAVE_PWM option in Konfig in the same directory

View/arch/arm/plat-s3c24xx/konfig

Config S3C24XX_PWM

BOOL "PWM Device Support"

Select HAVE_PWM

Help

Support for exporting the PWM timer blocks via the PWM device

System.

So when configuring the kernel make menuconfig, this item needs to be checked.

OK, let's see PWM.C, it's the PWM core driver, the drive does not separate the device and the drive, all written in this pwm.c, we first look at the driver part of PWM.C

static int __init pwm_init (void)

{

int ret;

Clk_scaler[0] = Clk_get (NULL, "Pwm-scaler0"); Get Clock number No. 0

CLK_SCALER[1] = Clk_get (NULL, "pwm-scaler1"); Get Clock number 1th

if (Is_err (clk_scaler[0)) | | Is_err (Clk_scaler[1])) {

PRINTK (kern_err "%s:failed to get Scaler clocks\n", __func__);

Return-einval;

}

ret = Platform_driver_register (&s3c_pwm_driver); Register PWM Driver

if (ret)

PRINTK (Kern_err "%s:failed to add PWM driver\n", __func__);

return ret;

}

Track down the definition of s3c_pwm_driver

static struct Platform_driver S3c_pwm_driver = {

. Driver = {

. Name = "S3C24XX-PWM",//driver name

. Owner = This_module,

},

. Probe = S3c_pwm_probe,//Probe function

. remove = __devexit_p (S3c_pwm_remove),

};

Let's look at the detection function s3c_pwm_probe

static int s3c_pwm_probe (struct platform_device *pdev)

{

struct Device *dev = &pdev->dev;

struct Pwm_device *PWM;

unsigned long flags;

unsigned long Tcon;

unsigned int id = pdev->id;

int ret;

if (id = = 4) {

Dev_err (Dev, "TIMER4 is currently not supported\n");

Return-enxio;

}

PWM = kzalloc (sizeof (struct pwm_device), gfp_kernel); Allocating PWM device Space

if (PWM = = NULL) {

Dev_err (Dev, "failed to allocate pwm_device\n");

Return-enomem;

}

Pwm->pdev = Pdev;

pwm->pwm_id = ID;

Pwm->tcon_base = id = = 0?  0: (ID * 4) + 4; Calculate which timer is controlled in the Tcon

PWM->CLK = Clk_get (Dev, "pwm-tin"); Get the clock after the Prescaler

if (Is_err (PWM->CLK)) {

Dev_err (Dev, "failed to get PWM tin clk\n");

ret = Ptr_err (PWM->CLK);

Goto Err_alloc;

}

Pwm->clk_div = Clk_get (Dev, "pwm-tdiv");

if (Is_err (Pwm->clk_div)) {//Get two time-divided clocks

Dev_err (Dev, "failed to get PWM tdiv clk\n");

ret = Ptr_err (PWM->CLK_DIV);

Goto Err_clk_tin;

}

Local_irq_save (flags);

Tcon = __raw_readl (S3c2410_tcon);

Tcon |= Pwm_tcon_invert (PWM); Signal reversal output

__raw_writel (Tcon, S3c2410_tcon);

Local_irq_restore (flags);

ret = Pwm_register (PWM); Registering a PWM device

if (ret) {

Dev_err (Dev, "failed to register pwm\n");

Goto Err_clk_tdiv;

}

pwm_dbg (PWM, "config bits%02x\n",

(__raw_readl (S3c2410_tcon) >> pwm->tcon_base) & 0x0f);

Dev_info (Dev, "Tin at%lu, Tdiv at%lu, TIN=%SCLK, base%d\n",

Clk_get_rate (PWM->CLK),

Clk_get_rate (Pwm->clk_div),

Pwm_is_tdiv (PWM)? "DIV": "ext", pwm->tcon_base);

Platform_set_drvdata (Pdev, PWM);

return 0;

Err_clk_tdiv:

Clk_put (PWM->CLK_DIV);

Err_clk_tin:

Clk_put (PWM->CLK);

Err_alloc:

Kfree (PWM);

return ret;

}

Here's a look at the functions of the registered PWM device Pwm_register

Static List_head (pwm_list);

static int pwm_register (struct pwm_device *pwm)

{

Pwm->duty_ns =-1;

Pwm->period_ns =-1;

Mutex_lock (&pwm_lock);

List_add_tail (&pwm->list, &pwm_list); Attach the PWM device to the Pwm_list list

Mutex_unlock (&pwm_lock);

return 0;

}

Let's see what this pwm.c gives us. What interface functions are available?

struct Pwm_device *pwm_request (int pwm_id, const char *label)

int pwm_config (struct pwm_device *pwm, int duty_ns, int period_ns)

int pwm_enable (struct pwm_device *pwm)

void Pwm_free (struct pwm_device *pwm)

Export_symbol (pwm_request); Request a PWM device

Export_symbol (Pwm_config); Configure PWM device, Duty_ns is empty, Period_ns is cycle

Export_symbol (pwm_enable); Start Timer timer

Export_symbol (pwm_disable); Turn off timer timer

Above this function, as long as know API, will call on the line, in this, I analyze the most difficult to configure a PWM function, this function is mainly based on the period Period_ns, calculate tcnt, according to the ratio of empty to Duty_ns, calculate tcmp, and then write the corresponding register.

int pwm_config (struct pwm_device *pwm, int duty_ns, int period_ns)

{

unsigned long tin_rate;

unsigned long Tin_ns;

unsigned long period;

unsigned long flags;

unsigned long Tcon;

unsigned long tcnt;

Long tcmp;

if (Period_ns > Ns_in_hz | | duty_ns > NS_IN_HZ)

Return-erange;

if (Duty_ns > Period_ns)

Return-einval;

if (Period_ns = = Pwm->period_ns &&

Duty_ns = = Pwm->duty_ns)

return 0;

tcmp = __raw_readl (S3C2410_TCMPB (pwm->pwm_id));

tcnt = __raw_readl (S3C2410_TCNTB (pwm->pwm_id));

period = Ns_in_hz/period_ns; Calculation period

pwm_dbg (PWM, "duty_ns=%d, period_ns=%d (%lu) \ n",

Duty_ns, Period_ns, period);

if (Pwm->period_ns! = Period_ns) {

if (Pwm_is_tdiv (PWM)) {

Tin_rate = Pwm_calc_tin (PWM, period);

Clk_set_rate (Pwm->clk_div, tin_rate);

} else

Tin_rate = Clk_get_rate (PWM->CLK);

Pwm->period_ns = Period_ns;

pwm_dbg (PWM, "tin_rate=%lu\n", tin_rate);

Tin_ns = ns_in_hz/tin_rate;

tcnt = Period_ns/tin_ns; Tcnt,n=to/ti according to the period

} else

Tin_ns = Ns_in_hz/clk_get_rate (PWM->CLK);

tcmp = Duty_ns/tin_ns; Seeking tcmp according to the ratio of empty to occupied

tcmp = tcnt-tcmp; Seeking tcmp according to duty ratio

if (tcmp = = tcnt)

tcmp--;

pwm_dbg (PWM, "Tin_ns=%lu, tcmp=%ld/%lu\n", Tin_ns, tcmp, tcnt);

if (tcmp < 0)

tcmp = 0;

Local_irq_save (flags);

__raw_writel (tcmp, S3C2410_TCMPB (pwm->pwm_id)); Write tcmp

__raw_writel (tcnt, S3C2410_TCNTB (pwm->pwm_id)); Write tcnt

Tcon = __raw_readl (S3c2410_tcon);

Tcon |= pwm_tcon_manulupdate (PWM);

Tcon |= pwm_tcon_autoreload (PWM); Auto Load

__raw_writel (Tcon, S3c2410_tcon);

Tcon &= ~pwm_tcon_manulupdate (PWM); Update tcnt and TCMP

__raw_writel (Tcon, S3c2410_tcon);

Local_irq_restore (flags);

return 0;

}

Let's talk about how this cycle is designed.

The output frequency of our timer is fi=pclk/(Prescaler value+1)/(divider value), which can be obtained by determining the value

We need to write an initial value of N to tcnt, so we can get a frequency, why?

According to the initial value n=fi/fo, then N=to/ti

So when the user passes a cycle period_ns to the Pwm_config function, it's actually To=period_ns

This n=to/ti= period_ns/fi according to the preceding formula, and then writes the initial value N to tcnt to change the cycle.

And then I'll add a note to the code comment in the Pwm_config function. What's going on with the automatic loading?

Timer working principle is actually tcnt value in the clock arrival, minus a count, each time minus one end, take the current tcnt and TCMP comparison, if the tcnt=tcmp, then the signal is vindicated to the output, and then tcnt continue to subtract a count, know tcnt minus to zero, If there is an auto-load function then the TCNTB will be the initial value of the count again to TCNTP, while TCMPB the comparative value to tcmp, so that the first initial reload, and then continue to count. We have called a double buffering mechanism for this loading mode, where TCMPB and TCNTB are buffer caches.

Front said PWM.C set drive and equipment in one, then we look at the device-related code

#define TIMER_RESOURCE_SIZE (1)

#define Timer_resource (_TMR, _IRQ) \

(struct resource [timer_resource_size]) { \

[0] = {\

. start = _irq, \

. end = _irq, \

. flags = IORESOURCE_IRQ \

}                                 \

}

#define Define_s3c_timer (_tmr_no, _IRQ) \

. Name = "S3C24XX-PWM", \

. id = _tmr_no, \

. num_resources = Timer_resource_size, \

. Resource = Timer_resource (_tmr_no, _IRQ), \

struct Platform_device s3c_device_timer[] = {

[0] = {define_s3c_timer (0, Irq_timer0)},

[1] = {Define_s3c_timer (1, Irq_timer1)},

[2] = {Define_s3c_timer (2, Irq_timer2)},

[3] = {Define_s3c_timer (3, Irq_timer3)},

[4] = {Define_s3c_timer (4, Irq_timer4)},

};

The above code is the device part of the code, in fact, is the resources of five timers, we look at the Define_s3c_timer macro, you will find its device name is "S3C24XX-PWM", and we defined in PWM.C the driver name is also "S3C24XX-PWM", So if we register the device with the kernel, then the device "S3C24XX-PWM" and the driver "S3C24XX-PWM" will match successfully. So if you use timer 0, you can just add s3c_device_timer[0 in the BSP]. We are now doing the buzzer driver, using the TIMER0 timer, we will add the following code in the mini2440 BSP file mach-mini2440.c

static struct Platform_device *mini2440_devices[] __initdata = {

......

&S3C_DEVICE_TIMER[0],//Add

};

So we can analyze the code of the PWM core layer.

Backlight subsystem under Linux (i) "Turn"

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.