media: video: tegra: as364x: add edp client
Charlie Huang [Tue, 18 Jun 2013 18:44:20 +0000 (11:44 -0700)]
enable edp support on as364x.

ported from http://git-master/r/#change,236030, which was submitted
already but cannot be simply cherry picked due to other differences.

bug 1299134

Change-Id: Iee54b554ae2b6cffef2cab31f23b6b5ffa2293d4
Signed-off-by: Charlie Huang <chahuang@nvidia.com>
Reviewed-on: http://git-master/r/239840
(cherry picked from commit fa919f0b3405eafde052483faa82ec66ef8e93da)
Reviewed-on: http://git-master/r/250881
Reviewed-by: Riham Haidar <rhaidar@nvidia.com>
Tested-by: Riham Haidar <rhaidar@nvidia.com>

drivers/media/video/tegra/as364x.c
include/media/as364x.h

index 81f80a5..e9b76e6 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/module.h>
 #include <linux/seq_file.h>
 #include <linux/debugfs.h>
+#include <linux/edp.h>
 #include <linux/regmap.h>
 #include <media/nvc.h>
 #include <media/as364x.h>
 #define AS364X_MAX_INDICATOR_CURRENT(x) \
                        DIV_ROUND_UP(((x) * 0xff * 0x3f / 0xff), 1000)
 
+#define GET_CURRENT_BY_INDEX(i, c)     \
+                       ((c) * (i)->dev_cap->curr_step_uA / 1000)
+#define GET_INDEX_BY_CURRENT(i, c)     \
+                       ((c) * 1000 / (i)->dev_cap->curr_step_uA)
+
 #define as364x_flash_cap_size \
                        (sizeof(struct nvc_torch_flash_capabilities_v1) \
                        + sizeof(struct nvc_torch_lumi_level_v1) \
@@ -140,6 +146,8 @@ struct as364x_info {
        struct nvc_torch_timer_capabilities_v1 *torch_timeouts[2];
        struct as364x_config config;
        struct as364x_reg_cache regs;
+       struct edp_client *edpc;
+       unsigned edp_state;
        atomic_t in_use;
        int flash_cap_size;
        int torch_cap_size;
@@ -301,6 +309,113 @@ static int as364x_reg_wr(struct as364x_info *info, u8 reg, u8 val)
        return err;
 }
 
+static void as364x_edp_lowest(struct as364x_info *info)
+{
+       if (!info->edpc)
+               return;
+
+       info->edp_state = info->edpc->num_states - 1;
+       dev_dbg(&info->i2c_client->dev, "%s %d\n", __func__, info->edp_state);
+       if (edp_update_client_request(info->edpc, info->edp_state, NULL)) {
+               dev_err(&info->i2c_client->dev, "THIS IS NOT LIKELY HAPPEN!\n");
+               dev_err(&info->i2c_client->dev,
+                       "UNABLE TO SET LOWEST EDP STATE!\n");
+       }
+}
+
+static void as364x_throttle(unsigned int new_state, void *priv_data);
+static void as364x_edp_register(struct as364x_info *info)
+{
+       struct edp_manager *edp_manager;
+       struct edp_client *edpc = &info->pdata->edpc_config;
+       int ret;
+
+       info->edpc = NULL;
+       if (!edpc->num_states) {
+               dev_notice(&info->i2c_client->dev,
+                       "%s: NO edp states defined.\n", __func__);
+               return;
+       }
+
+       strncpy(edpc->name, "as364x", EDP_NAME_LEN - 1);
+       edpc->name[EDP_NAME_LEN - 1] = 0;
+       edpc->throttle = as364x_throttle;
+       edpc->private_data = info;
+
+       dev_dbg(&info->i2c_client->dev, "%s: %s, e0 = %d, p %d\n",
+               __func__, edpc->name, edpc->e0_index, edpc->priority);
+       for (ret = 0; ret < edpc->num_states; ret++)
+               dev_dbg(&info->i2c_client->dev, "e%d = %d mA\n",
+                       ret - edpc->e0_index, edpc->states[ret]);
+
+       edp_manager = edp_get_manager("battery");
+       if (!edp_manager) {
+               dev_err(&info->i2c_client->dev,
+                       "unable to get edp manager: battery\n");
+               return;
+       }
+
+       ret = edp_register_client(edp_manager, edpc);
+       if (ret) {
+               dev_err(&info->i2c_client->dev,
+                       "unable to register edp client\n");
+               return;
+       }
+
+       info->edpc = edpc;
+       /* set to lowest state at init */
+       as364x_edp_lowest(info);
+}
+
+static int as364x_edp_req(struct as364x_info *info,
+               u8 mask, u8 *curr1, u8 *curr2)
+{
+       unsigned *estates;
+       unsigned total_curr = 0;
+       unsigned curr_mA;
+       unsigned approved;
+       unsigned new_state;
+       int ret = 0;
+
+       if (!info->edpc)
+               return 0;
+
+       dev_dbg(&info->i2c_client->dev, "%s: %d curr1 = %02x curr2 = %02x\n",
+               __func__, mask, *curr1, *curr2);
+       estates = info->edpc->states;
+       if (mask & 1)
+               total_curr += *curr1;
+       if (mask & 2)
+               total_curr += *curr2;
+       curr_mA = GET_CURRENT_BY_INDEX(info, total_curr);
+
+       for (new_state = info->edpc->num_states - 1; new_state > 0; new_state--)
+               if (estates[new_state] >= curr_mA)
+                       break;
+
+       dev_dbg(&info->i2c_client->dev,
+               "edp req: %d curr = %d mA\n", new_state, curr_mA);
+       ret = edp_update_client_request(info->edpc, new_state, &approved);
+       if (ret) {
+               dev_err(&info->i2c_client->dev, "E state transition failed\n");
+               return ret;
+       }
+
+       if (approved > new_state) { /* edp manager returned less current */
+               curr_mA = GET_INDEX_BY_CURRENT(info, estates[approved]);
+               if (mask & 1)
+                       *curr1 = curr_mA * (*curr1) / total_curr;
+               *curr2 = curr_mA - (*curr1);
+               dev_dbg(&info->i2c_client->dev,
+                       "new state: %d curr = %d mA (%d %d)\n",
+                       approved, curr_mA, *curr1, *curr2);
+       }
+
+       info->edp_state = approved;
+
+       return 0;
+}
+
 static int as364x_set_leds(struct as364x_info *info,
                        u8 mask, u8 curr1, u8 curr2)
 {
@@ -329,6 +444,10 @@ static int as364x_set_leds(struct as364x_info *info,
        } else
                curr2 = 0;
 
+       err = as364x_edp_req(info, mask, &curr1, &curr2);
+       if (err)
+               return err;
+
        regs[0] = curr1;
        regs[1] = curr2;
        regs[2] = info->regs.txmask;
@@ -343,6 +462,8 @@ static int as364x_set_leds(struct as364x_info *info,
        if (!err) {
                info->regs.led1_curr = curr1;
                info->regs.led2_curr = curr2;
+               if ((curr1 | curr2) == 0)
+                       as364x_edp_lowest(info);
        }
 
        dev_dbg(info->dev, "%s %x %x %x %x control = %x\n",
@@ -792,6 +913,7 @@ static int as364x_power_on(struct as364x_info *info)
 
        if (!err)
                info->power_on = 1;
+       as364x_edp_lowest(info);
 power_on_end:
        mutex_unlock(&info->mutex);
 
@@ -832,6 +954,7 @@ static int as364x_power_off(struct as364x_info *info)
 
        if (!err)
                info->power_on = 0;
+       as364x_edp_lowest(info);
 power_off_end:
        mutex_unlock(&info->mutex);
        return err;
@@ -886,6 +1009,16 @@ static int as364x_power(struct as364x_info *info, int pwr)
        return err;
 }
 
+static void as364x_throttle(unsigned int new_state, void *priv_data)
+{
+       struct as364x_info *info = priv_data;
+
+       if (!info)
+               return;
+
+       as364x_power(info, NVC_PWR_OFF);
+}
+
 static int as364x_get_dev_id(struct as364x_info *info)
 {
        int err;
@@ -1374,6 +1507,7 @@ static int as364x_probe(
        spin_unlock(&as364x_spinlock);
 
        as364x_power_get(info);
+       as364x_edp_register(info);
 
        err = as364x_get_dev_id(info);
        if (err < 0) {
index be887ad..9ab0301 100644 (file)
@@ -17,6 +17,7 @@
 #ifndef __AS364X_H__
 #define __AS364X_H__
 
+#include <linux/edp.h>
 #include <media/nvc_torch.h>
 
 enum {
@@ -84,6 +85,7 @@ struct as364x_platform_data {
        struct nvc_torch_pin_state pinstate; /* see notes in driver */
        unsigned gpio_strobe; /* GPIO connected to the ACT signal */
        bool strobe_low_act; /* strobe state active low */
+       struct edp_client edpc_config;
 
        int (*power_on_callback)(struct as364x_power_rail *pw);
        int (*power_off_callback)(struct as364x_power_rail *pw);