static ssize_t
geomagnetic_raw_offsets_show(struct device *dev,
        struct device_attribute *attr,
        char *buf)
{
    struct input_dev *input_raw = to_input_dev(dev);
    struct geomagnetic_data *data = input_get_drvdata(input_raw);
    struct yas_mag_offset offset;
    int accuracy;

    geomagnetic_multi_lock();

    offset = data->driver_offset;
    accuracy = atomic_read(&data->last_status);

    geomagnetic_multi_unlock();

    return sprintf(buf, "%d %d %d %d %d %d %d\n",
            offset.hard_offset[0],
            offset.hard_offset[1],
            offset.hard_offset[2],
            offset.calib_offset.v[0],
            offset.calib_offset.v[1],
            offset.calib_offset.v[2],
            accuracy);
}
static ssize_t
geomagnetic_filter_noise_store(struct device *dev,
        struct device_attribute *attr,
        const char *buf,
        size_t count)
{
    struct input_dev *input_raw = to_input_dev(dev);
    struct yas_mag_filter filter;
    struct geomagnetic_data *data = input_get_drvdata(input_raw);
    int32_t filter_noise[3];

    geomagnetic_multi_lock();

    sscanf(buf, "%d %d %d",
            &filter_noise[0],
            &filter_noise[1],
            &filter_noise[2]);
    hwdep_driver.get_filter(&filter);
    memcpy(filter.noise, filter_noise, sizeof(filter.noise));
    if (hwdep_driver.set_filter(&filter) == 0) {
        memcpy(data->filter_noise, filter_noise, sizeof(data->filter_noise));
    }

    geomagnetic_multi_unlock();

    return count;
}
static ssize_t
geomagnetic_raw_distortion_store(struct device *dev,
        struct device_attribute *attr,
        const char *buf,
        size_t count)
{
    struct input_dev *input_raw = to_input_dev(dev);
    struct geomagnetic_data *data = input_get_drvdata(input_raw);
    int32_t distortion[3];
    static int32_t val = 1;
    int i;

    geomagnetic_multi_lock();

    sscanf(buf, "%d %d %d",
            &distortion[0],
            &distortion[1],
            &distortion[2]);
    if (distortion[0] > 0 && distortion[1] > 0 && distortion[2] > 0) {
        for (i = 0; i < 3; i++) {
            data->distortion[i] = distortion[i];
        }
        input_report_abs(data->input_raw, ABS_RAW_DISTORTION, val++);
    }

    geomagnetic_multi_unlock();

    return count;
}
static ssize_t
geomagnetic_filter_threshold_store(struct device *dev,
        struct device_attribute *attr,
        const char *buf,
        size_t count)
{
    struct input_dev *input_data = to_input_dev(dev);
    struct geomagnetic_data *data = input_get_drvdata(input_data);
    struct yas_mag_filter filter;
    int value;


    if (hwdep_driver.get_filter == NULL || hwdep_driver.set_filter == NULL) {
        return -ENOTTY;
    }

    value = simple_strtol(buf, NULL, 10);

    if (geomagnetic_multi_lock() < 0) {
        return count;
    }

    hwdep_driver.get_filter(&filter);
    filter.threshold = value;
    if (hwdep_driver.set_filter(&filter) == 0) {
        data->filter_threshold = value;
    }

    geomagnetic_multi_unlock();

    return count;
}
static ssize_t
geomagnetic_filter_enable_store(struct device *dev,
        struct device_attribute *attr,
        const char *buf,
        size_t count)
{
    struct input_dev *input_data = to_input_dev(dev);
    struct geomagnetic_data *data = input_get_drvdata(input_data);
    int value;

    if (hwdep_driver.set_filter_enable == NULL) {
        return -ENOTTY;
    }

    value = simple_strtol(buf, NULL, 10);
    if (geomagnetic_multi_lock() < 0) {
        return count;
    }

    if (hwdep_driver.set_filter_enable(value) == 0) {
        data->filter_enable = !!value;
    }

    geomagnetic_multi_unlock();

    return count;
}
static ssize_t
geomagnetic_filter_len_show(struct device *dev,
        struct device_attribute *attr,
        char *buf)
{
    struct input_dev *input_data = to_input_dev(dev);
    struct geomagnetic_data *data = input_get_drvdata(input_data);
    int filter_len;

    geomagnetic_multi_lock();

    filter_len = data->filter_len;

    geomagnetic_multi_unlock();

    return sprintf(buf, "%d\n", filter_len);
}
static ssize_t
geomagnetic_raw_threshold_show(struct device *dev,
        struct device_attribute *attr,
        char *buf)
{
    struct input_dev *input_raw = to_input_dev(dev);
    struct geomagnetic_data *data = input_get_drvdata(input_raw);
    int threshold;

    geomagnetic_multi_lock();

    threshold = data->threshold;

    geomagnetic_multi_unlock();

    return sprintf(buf, "%d\n", threshold);
}
static ssize_t
geomagnetic_raw_shape_show(struct device *dev,
        struct device_attribute *attr,
        char *buf)
{
    struct input_dev *input_raw = to_input_dev(dev);
    struct geomagnetic_data *data = input_get_drvdata(input_raw);
    int shape;

    geomagnetic_multi_lock();

    shape = data->shape;

    geomagnetic_multi_unlock();

    return sprintf(buf, "%d\n", shape);
}
static ssize_t 
geomagnetic_raw_manual_offsets_show(struct device *dev,
        struct device_attribute *attr,
        char *buf)
{
    struct input_dev *input_raw = to_input_dev(dev);
    struct geomagnetic_data *data = input_get_drvdata(input_raw);
    struct yas_vector offset;

    geomagnetic_multi_lock();

    offset = data->manual_offset;

    geomagnetic_multi_unlock();

    return sprintf(buf, "%d %d %d\n", offset.v[0], offset.v[1], offset.v[2]);
}
/* Sysfs interface */
static ssize_t
geomagnetic_delay_show(struct device *dev,
        struct device_attribute *attr,
        char *buf)
{
    struct input_dev *input_data = to_input_dev(dev);
    struct geomagnetic_data *data = input_get_drvdata(input_data);
    int delay;

    geomagnetic_multi_lock();

    delay = data->delay;

    geomagnetic_multi_unlock();

    return sprintf(buf, "%d\n", delay);
}
static ssize_t
geomagnetic_raw_distortion_show(struct device *dev,
        struct device_attribute *attr,
        char *buf)
{
    struct input_dev *input_raw = to_input_dev(dev);
    struct geomagnetic_data *data = input_get_drvdata(input_raw);
    int rt;

    geomagnetic_multi_lock();

    rt = sprintf(buf, "%d %d %d\n",
            data->distortion[0],
            data->distortion[1],
            data->distortion[2]);

    geomagnetic_multi_unlock();

    return rt;
}
static ssize_t
geomagnetic_raw_transform_matrix_show(struct device *dev,
        struct device_attribute *attr,
        char *buf)
{
    struct input_dev *input_raw = to_input_dev(dev);
    struct geomagnetic_data *data = input_get_drvdata(input_raw);
    int32_t matrix[9];

    geomagnetic_multi_lock();

    memcpy(matrix, data->transform_matrix, sizeof(matrix));

    geomagnetic_multi_unlock();

    return sprintf(buf, "%d %d %d %d %d %d %d %d %d\n",
            matrix[0], matrix[1], matrix[2],
            matrix[3], matrix[4], matrix[5],
            matrix[6], matrix[7], matrix[8]);
}
static ssize_t
geomagnetic_enable_store(struct device *dev,
        struct device_attribute *attr,
        const char *buf,
        size_t count)
{
    struct input_dev *input_data = to_input_dev(dev);
    struct geomagnetic_data *data = input_get_drvdata(input_data);
    int value;

	dbg_func_in();
	
    value = !!simple_strtol(buf, NULL, 10);
	dbg("%s : enable(%d)\n", __func__, value);
    if (hwdep_driver.set_enable == NULL) {
        return -ENOTTY;
    }

#ifndef PANTECH_AVOID_DEADLOCK //p12911 : ef33s sensor patch
    if (geomagnetic_multi_lock() < 0) {
        return count;
    }
#endif

    if (hwdep_driver.set_enable(value) == 0) {
        if (value) {
            geomagnetic_enable(data);
        }
        else {
            geomagnetic_disable(data);
        }
    }

#ifndef PANTECH_AVOID_DEADLOCK
    geomagnetic_multi_unlock();
#endif

	dbg_func_out();

    return count;
}
static ssize_t
geomagnetic_raw_shape_store(struct device *dev,
        struct device_attribute *attr,
        const char *buf,
        size_t count)
{
    struct input_dev *input_raw = to_input_dev(dev);
    struct geomagnetic_data *data = input_get_drvdata(input_raw);
    int value = simple_strtol(buf, NULL, 10);

    geomagnetic_multi_lock();

    if (0 <= value && value <= 1) {
        data->shape = value;
        input_report_abs(data->input_raw, ABS_RAW_SHAPE, value);
    }

    geomagnetic_multi_unlock();

    return count;
}
static ssize_t
geomagnetic_raw_manual_offsets_store(struct device *dev,
        struct device_attribute *attr,
        const char *buf,
        size_t count)
{
    struct input_dev *input_raw = to_input_dev(dev);
    struct geomagnetic_data *data = input_get_drvdata(input_raw);
    struct yas_vector offset;

    geomagnetic_multi_lock();

    sscanf(buf, "%d %d %d", &offset.v[0], &offset.v[1], &offset.v[2]);
    if (hwdep_driver.set_manual_offset(&offset) == 0) {
        data->manual_offset = offset;
    }

    geomagnetic_multi_unlock();

    return count;
}
static int
geomagnetic_api_enable(int enable)
{
    struct geomagnetic_data *data = i2c_get_clientdata(this_client);
    int rt;

    if (geomagnetic_multi_lock() < 0) {
        return -1;
    }
    enable = !!enable;

    if ((rt = hwdep_driver.set_enable(enable)) == 0) {
        atomic_set(&data->enable, enable);
        if (enable) {
            rt = hwdep_driver.set_delay(20);
        }
    }

    geomagnetic_multi_unlock();

    return rt;
}
static ssize_t
geomagnetic_raw_transform_matrix_store(struct device *dev,
        struct device_attribute *attr,
        const char *buf,
        size_t count)
{
    struct input_dev *input_raw = to_input_dev(dev);
    struct geomagnetic_data *data = input_get_drvdata(input_raw);
    int32_t matrix[9];

    geomagnetic_multi_lock();

    sscanf(buf, "%d %d %d %d %d %d %d %d %d",
            &matrix[0], &matrix[1], &matrix[2],
            &matrix[3], &matrix[4], &matrix[5],
            &matrix[6], &matrix[7], &matrix[8]);
    if (hwdep_driver.set_transform_matrix(matrix) == 0) {
        memcpy(data->transform_matrix, matrix, sizeof(data->transform_matrix));
    }

    geomagnetic_multi_unlock();

    return count;
}
static ssize_t
geomagnetic_raw_offsets_store(struct device *dev,
        struct device_attribute *attr,
        const char *buf,
        size_t count)
{
    struct input_dev *input_raw = to_input_dev(dev);
    struct geomagnetic_data *data = input_get_drvdata(input_raw);
    struct yas_mag_offset offset;
    int32_t hard_offset[3];
    int i, accuracy;

    geomagnetic_multi_lock();

    sscanf(buf, "%d %d %d %d %d %d %d",
            &hard_offset[0],
            &hard_offset[1],
            &hard_offset[2],
            &offset.calib_offset.v[0],
            &offset.calib_offset.v[1],
            &offset.calib_offset.v[2],
            &accuracy);
    if (0 <= accuracy && accuracy <= 3) {
        for (i = 0; i < 3; i++) {
            offset.hard_offset[i] = (int8_t)hard_offset[i];
        }
        if (hwdep_driver.set_offset(&offset) == 0) {
            atomic_set(&data->last_status, accuracy);
            data->driver_offset = offset;
        }
    }

    geomagnetic_multi_unlock();

    return count;
}
static void
geomagnetic_input_work_func(struct work_struct *work)
{
    struct geomagnetic_data *data = container_of((struct delayed_work *)work,
            struct geomagnetic_data, work);
    uint32_t time_delay_ms = 100;
    struct yas_mag_offset offset;
    struct yas_mag_data magdata;
    int rt, i, accuracy;

     YLOGE(("[HSS] geomagnetic_input_work_func\n"));


    if (hwdep_driver.measure == NULL || hwdep_driver.get_offset == NULL) {
        return;
    }

    rt = hwdep_driver.measure(&magdata, &time_delay_ms);
    if (rt < 0) {
        YLOGE(("measure failed[%d]\n", rt));
    }
    YLOGE(("xy1y2 [%d][%d][%d] raw[%d][%d][%d]\n",
            magdata.xy1y2.v[0], magdata.xy1y2.v[1], magdata.xy1y2.v[2],
            magdata.xyz.v[0], magdata.xyz.v[1], magdata.xyz.v[2]));

    if (rt >= 0) {
        accuracy = atomic_read(&data->last_status);

        if ((rt & YAS_REPORT_OVERFLOW_OCCURED)
                || (rt & YAS_REPORT_HARD_OFFSET_CHANGED)
                || (rt & YAS_REPORT_CALIB_OFFSET_CHANGED)) {
            static uint16_t count = 1;
            int code = 0;
            int value = 0;

            hwdep_driver.get_offset(&offset);

            geomagnetic_multi_lock();
            data->driver_offset = offset;
            if (rt & YAS_REPORT_OVERFLOW_OCCURED) {
                atomic_set(&data->last_status, 0);
                accuracy = 0;
            }
            geomagnetic_multi_unlock();

            /* report event */
            code |= (rt & YAS_REPORT_OVERFLOW_OCCURED);
            code |= (rt & YAS_REPORT_HARD_OFFSET_CHANGED);
            code |= (rt & YAS_REPORT_CALIB_OFFSET_CHANGED);
            value = (count++ << 16) | (code);
            input_report_abs(data->input_raw, ABS_RAW_REPORT, value);
        }

        if (rt & YAS_REPORT_DATA) {
            for (i = 0; i < 3; i++) {
                atomic_set(&data->last_data[i], magdata.xyz.v[i]);
            }

            /* report magnetic data in [nT] */
            input_report_abs(data->input_data, ABS_X, magdata.xyz.v[0]);
            input_report_abs(data->input_data, ABS_Y, magdata.xyz.v[1]);
            input_report_abs(data->input_data, ABS_Z, magdata.xyz.v[2]);
            input_report_abs(data->input_data, ABS_STATUS, accuracy);
            input_sync(data->input_data);
        }

        if (rt & YAS_REPORT_CALIB) {
            /* report raw magnetic data */
            input_report_abs(data->input_raw, ABS_X, magdata.raw.v[0]);
            input_report_abs(data->input_raw, ABS_Y, magdata.raw.v[1]);
            input_report_abs(data->input_raw, ABS_Z, magdata.raw.v[2]);
            input_sync(data->input_raw);
        }
    }
    else {
        time_delay_ms = 100;
    }

    if (time_delay_ms > 0) {
        schedule_delayed_work(&data->work, msecs_to_jiffies(time_delay_ms) + 1);
    }
    else {
        schedule_delayed_work(&data->work, 0);
    }
}
static int
geomagnetic_work(struct yas_mag_data *magdata)
{
    struct geomagnetic_data *data = i2c_get_clientdata(this_client);
    uint32_t time_delay_ms = 100;
    struct yas_mag_offset offset;
    int rt, i, accuracy;

    if (hwdep_driver.measure == NULL || hwdep_driver.get_offset == NULL) {
        return time_delay_ms;
    }

    rt = hwdep_driver.measure(magdata, &time_delay_ms);
    if (rt < 0) {
        YLOGE(("measure failed[%d]\n", rt));
    }
    YLOGD(("xy1y2 [%d][%d][%d] raw[%d][%d][%d]\n",
            magdata->xy1y2.v[0], magdata->xy1y2.v[1], magdata->xy1y2.v[2],
            magdata->xyz.v[0], magdata->xyz.v[1], magdata->xyz.v[2]));

    if (rt >= 0) {
        accuracy = atomic_read(&data->last_status);

        if ((rt & YAS_REPORT_OVERFLOW_OCCURED)
                || (rt & YAS_REPORT_HARD_OFFSET_CHANGED)
                || (rt & YAS_REPORT_CALIB_OFFSET_CHANGED)) {
            static uint16_t count = 1;
            int code = 0;
            int value = 0;

            hwdep_driver.get_offset(&offset);

            geomagnetic_multi_lock();
            data->driver_offset = offset;
            if (rt & YAS_REPORT_OVERFLOW_OCCURED) {
                atomic_set(&data->last_status, 0);
                accuracy = 0;
            }
            geomagnetic_multi_unlock();

            /* report event */
            code |= (rt & YAS_REPORT_OVERFLOW_OCCURED);
            code |= (rt & YAS_REPORT_HARD_OFFSET_CHANGED);
            code |= (rt & YAS_REPORT_CALIB_OFFSET_CHANGED);
            value = (count++ << 16) | (code);
            input_report_abs(data->input_raw, ABS_RAW_REPORT, value);
        }

        if (rt & YAS_REPORT_DATA) {
            for (i = 0; i < 3; i++) {
                atomic_set(&data->last_data[i], magdata->xyz.v[i]);
            }

            /* report magnetic data in [nT] */
            input_report_abs(data->input_data, ABS_X, magdata->xyz.v[0]);
            input_report_abs(data->input_data, ABS_Y, magdata->xyz.v[1]);
            input_report_abs(data->input_data, ABS_Z, magdata->xyz.v[2]);
            input_report_abs(data->input_data, ABS_STATUS, accuracy);
            input_sync(data->input_data);
        }

        /* report raw magnetic data */
        input_report_abs(data->input_raw, ABS_X, magdata->raw.v[0]);
        input_report_abs(data->input_raw, ABS_Y, magdata->raw.v[1]);
        input_report_abs(data->input_raw, ABS_Z, magdata->raw.v[2]);
        input_sync(data->input_raw);
    }
    else {
        time_delay_ms = 100;
    }

    return time_delay_ms;

}