Analysis of Keypad Driver of Marvell (Marvell PXA310键盘驱动代码分析)

1.               Abstract

抱歉这篇报告是我当初用英文写的,有兴趣的朋友可以参考。

2.               Introduction

 

3.               Platform Codes

Document [1, 2, 3] is a good tip for readers to understanding the process of platform_device, platform_driver and platform_resources, and I suggest you have a rough look at it before going on if you do not have this kind of knowledge.

3.1        Initialize Codes

3.1.1          The Procedure to Register Keypad Deivce

Everything started from file: arch/arm/mach-pxa/generic.c, and the function is: pxa_init.

subsys_initcall(pxa_init); //Where subsys_initcall is called should be traced in the future.

static int __init pxa_init(void)

{

       int cpuid, ret;

       /* clear RDH */

       ASCR &= 0x7fffffff;

       pxa_clock_init();

       ret = platform_add_devices(devices, ARRAY_SIZE(devices));

       if (ret)

              return ret;

       /* Only add HWUART for PXA255/26x; PXA210/250/27x do not have it. */

       cpuid = read_cpuid(CPUID_ID);

       if (((cpuid >> 4) & 0xfff) == 0x2d0 ||

           ((cpuid >> 4) & 0xfff) == 0x290)

              ret = platform_device_register(&hwuart_device);

       return ret;

}

int platform_add_devices(struct platform_device **devs, int num)

{

       int i, ret = 0;

       for (i = 0; i < num; i++) {

              ret = platform_device_register(devs[i]);

              if (ret) {

                     while (--i >= 0)

                            platform_device_unregister(devs[i]);

                     break;

              }

       }

       return ret;

}

And let’s look at global variable devices (also in this file):

#ifdef CONFIG_PXA3xx

static struct platform_device *devices[] __initdata = {

……

       &keypad_device,

……

};

#else

static struct platform_device keypad_device = {

       .name            = "pxa3xx_keypad" ,

       .id          = -1,

       .resource      = keypad_resources,

       .num_resources   = ARRAY_SIZE(keypad_resources),

};

static struct resource keypad_resources[2] = {

       {

              .start = 0x41500000,

              .end   = 0x415000ff,

              .flags = IORESOURCE_MEM,

       }, {

              .start = IRQ_KEYPAD,

              .end   = IRQ_KEYPAD,

              .flags = IORESOURCE_IRQ,

       },

};

3.1.2          The Procedure to Add Platform Information to Device

Platform information is added into device in file: arch/arm/mach-pxa/littleton.c, after this function, mid2008_keypad_info is set to member of keypad_device.dev.platform_data .

static void __init littleton_init(void)

{

……

       /* Modified BEGIN, by  SHUYUN, on: 2007-12-18 16:29:41 */

       #if 0

       pxa_set_keypad_info(&littleton_keypad_info);

       /*=========== replaced by ===================*/

       #else

       pxa_set_keypad_info(&mid2008_keypad_info);

       #endif

       /* Modified END, by  SHUYUN, on: 2007-12-18 16:29:41 */

}

littleton_init is defined in following codes:

MACHINE_START(LITTLETON, "Marvell Form Factor Development Platform (aka Littleton)")

#ifdef CONFIG_CPU_PXA310

       .phys_io = 0x40000000,

       .boot_params       = 0xb0000100,

#else

       .phys_io        = 0x40000000,

       .boot_params    = 0x80000100,

#endif

       .io_pg_offst    = (io_p2v(0x40000000) >> 18) & 0xfffc,

       .map_io         = littleton_map_io,

       .init_irq       = littleton_init_irq,

       .timer          = &pxa_timer,

       .init_machine   = littleton_init,

MACHINE_END

After extracted, the codes are:

static const struct machine_desc __mach_desc_LITTLETON

  __attribute_used__                             

  __attribute__((__section__(".arch.info.init"))) = {  

       .nr          = MACH_TYPE__LITTLETON,        

       .name            = "Marvell Form Factor Development Platform (aka Littleton)",

       .phys_io = 0x40000000,

       .boot_params       = 0xb0000100,

       .io_pg_offst    = (io_p2v(0x40000000) >> 18) & 0xfffc,

       .map_io         = littleton_map_io,

       .init_irq       = littleton_init_irq,

       .timer          = &pxa_timer,

       .init_machine   = littleton_init,

};

We now come back to mid2008_keypad_info, the definition of it is (also in file: arch/arm/mach-pxa/littleton.c, however the data structure is defined in include/asm-arm/arch-pxa/pxa3xx_keypad.h):

static struct pxa3xx_keypad_platform_data mid2008_keypad_info = {

       .enable_repeat_key    = 1,

       .enable_direct_key     = 1,

       .enable_rotary_key     = 1,

       .matrix_key_debounce      = 30,

       .direct_key_debounce       = 30,

       .matrix_key_rows      = 6,

       .matrix_key_cols = 5,

       .direct_key_num        = 2,

       .matrix_key_map            = mid2008_key_map,

       .matrix_key_map_size      = ARRAY_SIZE(mid2008_key_map),

       .direct_key_map        = NULL,

       .direct_key_map_size       = -1,

       .rotary_up_key         = KEY_SCROLLUP,

       .rotary_down_key    = KEY_SCROLLDOWN,

};

The definition of mid2008_key_map is:

static unsigned int mid2008_key_map[] = {

       /* KEY(row in, col out, key_code) */

       KEY(1, 1, KEY_UP),

       KEY(3, 2, KEY_DOWN),

       KEY(0, 2, KEY_LEFT),

       KEY(2, 1, KEY_RIGHT),

       KEY(3, 1, KEY_ENTER),

       KEY(0, 3, KEY_OK),      //rotary key press

       KEY(0, 1, KEY_F5),       //menu

       KEY(2, 2, KEY_F4),       //HOME

       KEY(1, 2, KEY_MENU),       //mouse right key

       KEY(0, 0, KEY_F6),

       KEY(2, 0, KEY_F7),

       KEY(3, 0, KEY_F8),

       KEY(4, 1, KEY_POWER),

       KEY(4, 3, KEY_CLOSE),

};

We will see in the future that this member is passed to driver for key mapping.

4.               Driver Codes

4.1        Data Structure

struct pxa3xx_keypad {

       struct pxa3xx_keypad_platform_data *pdata;

       struct input_dev *input_dev;

       void __iomem *mmio_base;

       /* matrix key code map */

       unsigned int matrix_key_map[MAX_MATRIX_KEY_NUM];

       /* state row bits of each column scan */

       uint32_t matrix_key_state[MAX_MATRIX_KEY_COLS];

       uint32_t direct_key_state;

       unsigned int rotary_count;

       /* Added BEGIN, by  SHUYUN, on: 2007-12-21 11:09:23 */

       /* timer for rotary */

       #ifdef TIMER_FOR_ROTARY_DELAY

       struct timer_list timer_rotary;

       spinlock_t rotary_lock;

       #endif

       /* Added END, by  SHUYUN, on: 2007-12-21 11:09:23 */

};

This data structure is set to pdev->dev.driver_data in function pxa3xx_keypad_probe:

platform_set_drvdata(pdev, keypad);

4.2        Initialize Codes

The initialize codes of keypad driver is:

static int __init pxa3xx_keypad_init(void)

{

       return platform_driver_register(&pxa3xx_keypad_driver);

}

static struct platform_driver pxa3xx_keypad_driver = {

       .driver           = {

              .name     = "pxa3xx_keypad" , //Please notice that it is same as the device name

       },

       .probe          = pxa3xx_keypad_probe,

       .remove        = pxa3xx_keypad_remove,

       .suspend       = pxa3xx_keypad_suspend,

       .resume         = pxa3xx_keypad_resume,

};

4.3        Probe Function

Please refer to comments following the codes.

static int __init pxa3xx_keypad_probe(struct platform_device *pdev)

{

       struct pxa3xx_keypad *keypad;

       struct input_dev *input_dev;

       struct resource *res;

       int ret;

       keypad = kzalloc(sizeof(struct pxa3xx_keypad), GFP_KERNEL); //Please refer to the definition above

       if (!keypad)

              return -ENOMEM;

       input_dev = input_allocate_device(); //This is a sub-routine provided by input sub-system

       ……

       the_pxa3xx_keypad = keypad;       //save the handle for future use by headset detect

       keypad->input_dev = input_dev;

       res = platform_get_resource(pdev, IORESOURCE_MEM , 0);//You should now understand how resources are passed to device driver, please refer to section 3.1.1. Resource is first defined in device data structure, and now it is passed to driver.

       res = request_mem_region(res->start, res->end - res->start + 1,

                            "pxa3xx_keypad"); //Get the IO Resource

       ……

       /* Added BEGIN, by  SHUYUN, on: 2007-12-21 11:09:23 */

       /* initialize timer */

       //Following codes initialize the rotary key, which is invoked when there is an interrupt on rotary key and which is used to calculate how many rotaries happened during the delay. 

       #ifdef TIMER_FOR_ROTARY_DELAY

       spin_lock_init(&keypad->rotary_lock);

       init_timer(&keypad->timer_rotary);

       keypad->timer_rotary.expires = jiffies + msecs_to_jiffies(300); // Here I am amazing why it is first executed when module is initialized.

       keypad->timer_rotary.function = enable_keypad_irq;

       keypad->timer_rotary.data = (unsigned long)keypad;

       #endif

       /* Added END, by  SHUYUN, on: 2007-12-21 11:09:23 */

       keypad->mmio_base = ioremap_nocache(res->start, KEYPAD_MMIO_SIZE); //It is a little confused since we have already used the map_desc mechanism, and we could use the IO resources directly without need to call ioremap_nocache. Why? Finally I figured out the reason is that we have not used the map_desc mechanism, the variable standard_io_desc defined in arch/arm/mach-pxa/generic.c is not the map_desc used in our system. From section 3.1.2 (The map_io defined in BEGIN_MACHINE part in arch/arm/mach-pxa/littleton.c), we may see that the map_desc used in our system is: littleton_map_io, however, which is defined as a NULL array in file arch/arm/mach-pxa/littleton.c

       ……

       ret = request_irq(IRQ_KEYPAD, pxa3xx_keypad_interrupt, /*IRQF_DISABLED*/0, "pxa3xx_keypad", keypad); //This part is not so formal, the official way should fetch the IRQ from keypad resources, just like what is done in getting IO resources.

       ……

       platform_set_drvdata(pdev, keypad); // pdev->dev.driver_data now point to keypad

       /* setup input device */

       input_dev->name = "pxa3xx_keypad";

       input_dev->phys = "pxa3xx_keypad/input0";

       input_dev->cdev.dev = &pdev->dev;

       input_dev->private = keypad;

 

       /* initialize the keypad controller with platform

         * specific parameters and build matrix key code

         * map for fast lookup

         */

       keypad->pdata = pdev->dev.platform_data; //From section 3.1, you already got what the platform_data is, please go back to the end of section 3.1.2 for details.

       pxa3xx_keypad_build_keymap(keypad);//Build the keymap, please refer to the end of section 3.1.2 for details.

       set_bit(keypad->pdata->rotary_up_key, keypad->input_dev->keybit);

       set_bit(keypad->pdata->rotary_down_key, keypad->input_dev->keybit);

       set_bit(EV_KEY, input_dev->evbit);

       if (keypad->pdata->enable_repeat_key)

              set_bit(EV_REP, input_dev->evbit); //The above five line codes is better moved to function pxa3xx_keypad_build_keymap.

 

       /* Added BEGIN, by  SHUYUN, on: 2008-2-28 9:32:10 */

       /* set keypad led bit in input device struct*/

       #if 1

       //set led bit

       set_bit(EV_LED, input_dev->evbit);     //input_event.type

       set_bit(LED_MISC, input_dev->ledbit);  //input_event.code

       //set call back function

       input_dev->event = pxa3xx_keypad_event; //Write interface which is used to control LED

       //set gpio for led

       pxa3xx_mfp_set_afds(MFP_KEYPAD_LED, MFP_AF0, MFP_DS04X);

       pxa3xx_gpio_set_direction(MFP_KEYPAD_LED, GPIO_DIR_OUT);

       // for default , shutdown led

       pxa3xx_gpio_set_level(MFP_KEYPAD_LED, GPIO_LEVEL_LOW);

       //I have not go into details of the above three lines of LED.

       #endif

       /* Added END, by  SHUYUN, on: 2008-2-28 9:32:10 */

       set_bit(EV_SW, input_dev->evbit);

       set_bit(SW_HEADPHONE_INSERT, keypad->input_dev->swbit);

       ret = input_register_device(input_dev); //Register the device to input sub-system

       ……

       pxa_set_cken(CKEN_KEYPAD, 1); //Set the keypad clock. Unfortunately I have not read the Marvell Clock Spec part, and therefore I can not understand this part.

       pxa3xx_keypad_config(keypad); //This function initialize the keypad register, which will be explained later.

#ifdef HEADSET_DETECT_USE_GPIO

       set_headset_detect_gpio();

#else

       micco_headset_detect_init();//I have not gone into details of this code, since I have no background knowledge now.

#endif

       /* Added BEGIN, by  SHUYUN, on: 2007-12-21 14:39:14 */

       /* bug for rotary key: should initializa the first value store in keypad->rotary_count */

       #if 1

       keypad->rotary_count = DEFAULT_ROTARY_COUNT;

       #endif

       /* Added END, by  SHUYUN, on: 2007-12-21 14:39:14 */

       return 0;

       ……

}

4.4        Function pxa3xx_keypad_config

Please refer to comments following the codes.

static void pxa3xx_keypad_config(struct pxa3xx_keypad *keypad)

{

       struct pxa3xx_keypad_platform_data *pdata = keypad->pdata;

       uint32_t kpc = 0, debounce = 0;

       /* enable matrix key with automatic scan */

       kpc = KPC_MKRN(pdata->matrix_key_rows) |

             KPC_MKCN(pdata->matrix_key_cols); //Set the row line numbers and column line numbers in our system, which is provided in platform information (6 rows and 5 columns), see section 3.1.2. Please refer to document [4] P409 for the register KPC[28..26, 25..23] information.

       kpc |= KPC_ASACT | KPC_MIE | KPC_ME | KPC_MS_ALL; //Enalbe Automatic Scan on Activity(KPC[29]), Matrix Interrupt (KPC[11]), Matrix Keypad (KPC[12]), and Matrix Scan Line 0~7 (Column lines, KPC[20..13]). Please refer to document [4] P409~411. The most important information is the meaning of ASACT, and you may refer to document[4] P403 5.4.1.2 for its introduction.

       /* enable direct key */

       if (pdata->enable_direct_key) {

              /* Modified BEGIN, by  SHUYUN, on: 2007-12-20 21:20:05 */

              #ifdef DIRECT_KEY_DEBOUNCE_EQUAL_MATRIX

              kpc |= KPC_DKN(pdata->direct_key_num) | KPC_DE | KPC_DIE;

              #else  //add debounce for directory key and rotary key--KPC_DK_DEB_SEL

              kpc |= KPC_DKN(pdata->direct_key_num) | KPC_DE | KPC_DIE | KPC_DK_DEB_SEL;//Set the direct Key number to 2 (KPC[8..6]=001), enable Direct Key (KPC[1]) and Direct Key Interrupt (KPC[0]), and set the key debounce time of direct key to the corresponding field of direct key in KPKDI register (KPC[9]= 1). Please refer to document [4] P409~411.

              #endif

              /* Modified END, by  SHUYUN, on: 2007-12-20 21:20:05 */

    }

          /* enable rotary key if necessary */

       if (pdata->enable_rotary_key) {

              /* Modified BEGIN, by  SHUYUN, on: 2007-12-10 10:32:42 */

              #ifdef ROTARY_ZERO_DEBOUNCE  //after testing,rotary key debounce 0 works well

              kpc |= (KPC_REE0 | KPC_RE_ZERO_DEB);//Enable REE A, which is rotary A (KPC[2]), and set debounce time to 0 (KPC[4]). And therefore the setting of KPC_DK_DEB_SEL in last line codes does not make any sense. When I reach this code, I finally understand why we should use a timer for rotary key, since we have not set any stable time and therefore we can not read the rotary number directly.

              #else //rotary encoder debounce is equal to the direct keypad debounce

              kpc |= KPC_REE0;

              #endif

              /* Modified END, by  SHUYUN, on: 2007-12-10 10:32:42 */

    }

          keypad_writel(keypad->mmio_base, KPC, kpc);//Write the KPC register

       keypad_writel(keypad->mmio_base, KPREC, DEFAULT_ROTARY_COUNT); //Write the rotary count for Key A (KPC[7..0]), and I am amazing why we do not set it to OxFF and therefore we may collect more rotary counts before overflow? (Now it is 0x7F).

       debounce = ((pdata->direct_key_debounce & 0xff) << 8) |

                  (pdata->matrix_key_debounce & 0xff);

       keypad_writel(keypad->mmio_base, KPKDI, debounce); //Write the debounce register (KPKDI[15..8] and KPKDI[7..0]), both of them are set to 30ms.

       //FIXME, not needed, really?

       //pxa3xx_enable_keyp_pins();

}

4.5        Function pxa3xx_keypad_interrupt

Please refer to comments following the codes.

static irqreturn_t pxa3xx_keypad_interrupt(int irq, void *dev_id)

{

       struct pxa3xx_keypad *keypad = dev_id;

       uint32_t kpc;

              spin_lock_irq(&keypad->rotary_lock);

       kpc = keypad_readl(keypad->mmio_base, KPC);

       ……

       if (kpc & KPC_MI) { //If the event belongs to matrix key

              pxa3xx_keypad_scan_matrix(keypad);

       }

       if (kpc & KPC_DI) { //If the event belongs to direct key

              pxa3xx_keypad_scan_direct(keypad);

       }

       //driver all colum to hight

       kpc = keypad_readl(keypad->mmio_base, KPC);

       kpc |= KPC_MS_ALL;//Enable column setting for auto-scan

       keypad_writel(keypad->mmio_base, KPC, kpc);

       spin_unlock_irq(&keypad->rotary_lock);

       return IRQ_HANDLED;

}

4.6        Function pxa3xx_keypad_scan_matrix

Please refer to comments following the codes.

static void pxa3xx_keypad_scan_matrix(struct pxa3xx_keypad *keypad)

{

       int row, col, num_keys_pressed = 0;

       uint32_t new_state[MAX_MATRIX_KEY_COLS];

       uint32_t kpas = keypad_readl(keypad->mmio_base, KPAS); //Read in KPAS Register.

       num_keys_pressed = KPAS_MUKP(kpas); //Get the pressed key number. If KPC. IMKP (KPC[21]) is not set, multi-key is permitted. KPAS[30..26] then indicates if there are more than one keys. Note: We have not check KPAS.SO (KPAS[31]) before doing this, but it is required in SPEC. Please refer to document [4] P416 section 5.5.5 for details.

       memset(new_state, 0, sizeof(new_state));

       if (num_keys_pressed == 0)

       {

              goto scan_finished;

       }

       if (num_keys_pressed == 1) { //Only one key is pressed

              col = KPAS_CP(kpas); //KPAS[3..0] indicates the pressed key column number.

              row = KPAS_RP(kpas); //KPAS[7..4] indicates the pressed key row number.

              ……

              new_state[col] = (1 << row);

              goto scan_finished;

       }

       if (num_keys_pressed > 1) {//More than one keys are pressed. KPASMKP0~KPASMKP3 are used to store them with each one stores two column statuses, which stores in KPASMKPx[23..16] and KPASMKPx[7..0]. SPEC has required us to test KPASMKPx.SO (KPASMKPx[31]), yet we have not done it.

              uint32_t kpasmkp0 = keypad_readl(keypad->mmio_base, KPASMKP0);

              uint32_t kpasmkp1 = keypad_readl(keypad->mmio_base, KPASMKP1);

              uint32_t kpasmkp2 = keypad_readl(keypad->mmio_base, KPASMKP2);

              uint32_t kpasmkp3 = keypad_readl(keypad->mmio_base, KPASMKP3);

              new_state[0] = kpasmkp0 & KPASMKP_MKC_MASK;

              new_state[1] = (kpasmkp0 >> 16) & KPASMKP_MKC_MASK;

              new_state[2] = kpasmkp1 & KPASMKP_MKC_MASK;

              new_state[3] = (kpasmkp1 >> 16) & KPASMKP_MKC_MASK;

              new_state[4] = kpasmkp2 & KPASMKP_MKC_MASK;

              new_state[5] = (kpasmkp2 >> 16) & KPASMKP_MKC_MASK;

              new_state[6] = kpasmkp3 & KPASMKP_MKC_MASK;

              new_state[7] = (kpasmkp3 >> 16) & KPASMKP_MKC_MASK;

       }

scan_finished: //Now that we have finished getting the status, we then report them through input sub-system.

       for (col = 0; col < MAX_MATRIX_KEY_COLS; col++) {

              uint32_t bits_changed;

                     bits_changed = keypad->matrix_key_state[col] ^ new_state[col]; //Find if this column changed.

              if (bits_changed == 0)

                     continue;

              for (row = 0; row < MAX_MATRIX_KEY_ROWS; row++) {

                     if ((bits_changed & (1 << row)) == 0) //Find if this row changed.

                            continue;

                     input_report_key(keypad->input_dev,

                            lookup_matrix_keycode(keypad, row, col),

                            new_state[col] & (1 << row)); //Report the key.

              }

       }

       memcpy(keypad->matrix_key_state, new_state, sizeof(new_state));

}

4.7        Function pxa3xx_keypad_scan_direct

Please refer to comments following the codes.

static void pxa3xx_keypad_scan_direct(struct pxa3xx_keypad *keypad)

{

       struct pxa3xx_keypad_platform_data *pdata = keypad->pdata;

       int i, start_key = 0;

       unsigned int new_state;

       uint32_t kpdk = 0, bits_changed;

       uint32_t kpc = keypad_readl(keypad->mmio_base, KPC); //Read in register KPC

       kpdk = keypad_readl(keypad->mmio_base, KPDK); //Read in register KPDK, which stores the input value of direct key.

       /* Deleteed BEGIN, by  SHUYUN, on: 2008-3-14 16:44:19 */

       #if 0

       if (!(kpdk & KPDK_DKP)) {// It is amazing, since KPDK.DPK (KPDK[31]) should be checked according to document [4], why here it is commented out?

              return;

       }    

       #endif

       /* Deleted END, by  SHUYUN, on: 2008-3-14 16:44:19 */

       if (pdata->enable_rotary_key) { //We have set it, refer to section 3.1.2

        /* Added BEGIN, by  SHUYUN, on: 2007-12-21 10:51:04 */

        #ifdef TIMER_FOR_ROTARY_DELAY

        //disable direct key interrupt first

        kpc &= ~(KPC_DIE); //Disable the direct key interruption. KPC[0]

        keypad_writel(keypad->mmio_base, KPC, kpc); 

         //mod timer for delay intervals--pay attention:never use add_timer

        mod_timer(&keypad->timer_rotary, jiffies + msecs_to_jiffies(200)); //Start the timer, and the callback is enable_keypad_irq.

        #endif

        /* Added END, by  SHUYUN, on: 2007-12-21 10:51:04 */

        pxa3xx_keypad_scan_rotary(keypad); //This function is NULL.

        /* direct key 0 & 1 are used by rotary encoder */

        start_key = 2; //Skip rotary Key A, since they are detected in the timer callback.

       }

          if (pdata->direct_key_map == NULL) //We returned here.

              return;

       new_state = KPDK_DK(kpdk); //Get the stored key value in KPDK, which is KPDK[7..0], following is the same as direct key process, and need not to explain any more.

       bits_changed = keypad->direct_key_state ^ new_state;

       if (bits_changed == 0)

              return;

       for (i = start_key; i < MAX_DIRECT_KEYS; i++) {

              if (bits_changed & (1 << i))

                     input_report_key(keypad->input_dev,

                                   pdata->direct_key_map[i],

                                   (new_state & (1 << i)));

       }

       keypad->direct_key_state = new_state;

}

4.8        Function enable_keypad_irq

This function is the timer call back function of direct key. Please refer to comments following the codes.

void enable_keypad_irq(unsigned long ptr) {

       struct pxa3xx_keypad *keypad = (struct pxa3xx_keypad *)ptr;

       struct pxa3xx_keypad_platform_data *pdata = keypad->pdata;

       uint32_t kpc = 0;

       uint32_t kprec = 0;

       unsigned int new_rotary_count;

       unsigned int i;

       unsigned int key_code;

       unsigned int rotary_number = 1;

       kprec = keypad_readl(keypad->mmio_base, KPREC); //Read in KPREC, which is the register to store Rotary Encoder Count.

       new_rotary_count = KPREC_RECOUNT0(kprec);   //KPREC[7..0] is used to store the current count value.

       if (kprec & (KPREC_UF0 | KPREC_OF0)) {//Test if it has underflow or overflow.

              /* overflow/underflow: set old count to overflow or underflow thresh value */

              keypad->rotary_count = ( kprec & KPREC_UF0 ) ? 0xFF:0x0; //Reset the number. I do not know why here we do not send the rotary count between current count to 0 or max-value first before reset the count?

       }

       if (new_rotary_count > keypad->rotary_count){ //Positive rotate

              key_code = pdata->rotary_up_key;

              rotary_number = new_rotary_count - keypad->rotary_count;

       }else{ //Negative rotate

              key_code = pdata->rotary_down_key;

              rotary_number = keypad->rotary_count - new_rotary_count;

       }

       keypad->rotary_count = new_rotary_count;

       // don't know why, but every scoll actually change rotary_count by 4, so we set increment to 4

       for ( i = 0; i< rotary_number; i += 4) //Send out the rotary key

       {

              /* simulate a press-n-release */

              input_report_key(keypad->input_dev, key_code, 1);

              input_report_key(keypad->input_dev, key_code, 0);

       }

       spin_lock_irq(&keypad->rotary_lock);

       //enable direct interrupt

       kpc = keypad_readl(keypad->mmio_base, KPC);

       kpc |= (KPC_DIE); //Enable the direct key interrupt ( KPC[0]), since which is disabled in 

       keypad_writel(keypad->mmio_base, KPC, kpc);

       spin_unlock_irq(&keypad->rotary_lock);

}

4.9        Function pxa3xx_keypad_event

This function is used for LED Setting, and I have not analysis it now since I do not know the hardware connection. It seemed that it is just GPIO setting.

4.10     Function pmic_headset_interrupt

This function is called by micco_headset_detect_init

void pmic_headset_interrupt(unsigned long event)

{

       int ret = 0;

       u8 val;

       ret = micco_read(MICCO_STATUS_B, &val); //Check if this event is headset plugin or plugout. Please refer to document [5] P124 Section STATUS_B Register for details.

       if (val & ( MICCO_STATUS_B_HEADSET) ){

              input_report_switch(the_pxa3xx_keypad->input_dev, SW_HEADPHONE_INSERT, 1);

       }else{

              input_report_switch(the_pxa3xx_keypad->input_dev, SW_HEADPHONE_INSERT, 0);

       }

       //printk( " %s : STATUS_B : %d /n", __FUNCTION__,val);

       return;

}

4.11     Function micco_headset_detect_init

This function is called by pxa3xx_keypad_probe

int micco_headset_detect_init(void)

{

       int ret = 0;

       u8 val;

       //printk(" %s : %s enter /n", __FILE__ , __FUNCTION__);

       ret = micco_read(MICCO_IRQ_MASK_D, &val);

       val &= ~(IRQ_MASK_D_HEADSET);

       ret = micco_write(MICCO_IRQ_MASK_D, val); //Enable headset interruption. Please refer to document [5] P125 Section nIRQ_MASK_D Register for details.

       ret = micco_read(MICCO_MISC, &val);

       val |= MICCO_MISC_REMCON_AUTO;

       val &= ~(MICCO_MISC_REMCON_EN );//Set Automatic Polling Mode, and clear Continuous Monitoring Mode. Please refer to document [5] P94 Section Automatic Polling Mode and Section Continuous Monitoring Mode. Please refer to section 7.1

       ret = micco_write(MICCO_MISC, val);

       ret = pmic_callback_register(PMIC_EVENT_HEADSET, pmic_headset_interrupt); //Register the interruption call back of headset. Please refer to document [5] P124 Section Event_D Register for details. Please refer to my document [6] and [7] if you do not familiar with the backgrounds

       ……

       return ret;

      

}

5.               Input Sub-system

6.               Misc Points

 

7.               Questions

7.1        What is the meaning of REMCON_AUTO and REMCON_EN?

Place: P94of Data Sheet: DA9034 Audio and Power Management IC

Following is the description on P94. However, after reading this, I still do not understand it.

Automatic Polling Mode

This mode of operation is enabled by the REMCON_AUTO bit in the I2C MISC_B register. The status of the REM_IN pin is polled once per second, with the circuitry powered up for a period of 10 ms. The LDO_AUDIO, Microphone bias amplifier and the MICBIAS_EXT switch are automatically enabled if not already active, then disabled after the measurement is completed.

If a change in the status of either the HEADSET or HOOK_SWITCH is detected after the 10 ms period, an optional 30 ms is waited to de-bounce the signals (selected by the REMCON_FILTER I2C bit), after which the circuit will issue an nIRQ and event, or not as the case may be.

HEADSET detection carried out by comparing the voltage at the REM_IN node to a reference voltage that is 90% of MICBIAS. A high level is detected as no external microphone present, while a low level is detected as a microphone present.

HOOK-SWITCH detection is carried out by comparing the voltage at the REM_IN node to a reference voltage that is 10% of MICBIAS. A high level is detected as non-pressed button, while a low level is detected as a pressed button.

Continuous Monitoring Mode

This mode of operation is enabled by the REMCON bit in the I2C MISC_B register. The status of the REM_IN pin is continuously monitored, with the MICBIAS_EXT pin enabled along with the MICBIAS amplifier and LDO_AUDIO etc. If a change in the status of either the HEADSET or HOOK_SWITCH is detected, an optional 30 ms is waited to de-bounce the signals (selected by the REMCON_FILTER I2C bit), after which the circuit will issue an nIRQ and event, or not as the case may be. The continuous monitoring mode over-rides the automatic polling mode.

HEADSET detection carried out by comparing the voltage at the REM_IN node to a reference voltage that is 90% of MICBIAS. A high level is detected as no external microphone present, while a low level is detected as a microphone present.

HOOK-SWITCH detection is carried out by comparing the voltage at the REM_IN node to a reference voltage that is 10% of MICBIAS. A high level is detected as non-pressed button, while a low level is detected as a pressed button.

8.               References

[1]. struct resource struct platform_device 和驱动的关系? , http://www.hhcn.com/cgi-bin/topic.cgi?forum=3&topic=437

[2]. platform_device platform_driver (一) , http://blog.chinaunix.net/u1/57747/showart_1073860.html

[3]. platform_device platform_driver (二) , http://blog.chinaunix.net/u1/57747/showart_1074059.html

[4]. Monahans_L_LV_Processor_Dev_Man_Vol_III

[5]. Data Sheet: DA9034 Audio and Power Management IC

[6]. Analysis of I2C.doc, Huang Gao

[7]. Analysis of Touch Panel Driver.doc, Huang Gao

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值