summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorXinyu Chen <b03824@freescale.com>2012-05-25 17:06:07 +0800
committerXinyu Chen <b03824@freescale.com>2012-05-25 17:06:07 +0800
commitfd60e6b4548d6e31beff9a959148653481b2e695 (patch)
treee64a630c6aafd3723dfd7f293ab970f0aaf17418 /drivers
parent411d6f671b216f7127284349d9c30a6cca88a15d (diff)
parent79f31695fcba11cb303e039eddaea82197416368 (diff)
Merge remote branch 'fsl-linux-sdk/imx_3.0.15' into imx_3.0.15_android
Conflicts: arch/arm/configs/imx6_defconfig arch/arm/kernel/smp.c arch/arm/mach-mx6/board-mx6q_arm2.c arch/arm/mach-mx6/board-mx6q_sabreauto.c arch/arm/mach-mx6/board-mx6q_sabrelite.c arch/arm/mach-mx6/board-mx6q_sabresd.c arch/arm/mach-mx6/bus_freq.c arch/arm/mach-mx6/clock.c arch/arm/mach-mx6/cpu.c arch/arm/mach-mx6/devices-imx6q.h arch/arm/mach-mx6/mx6_ddr_freq.S arch/arm/mach-mx6/mx6_mmdc.c arch/arm/mach-mx6/mx6_suspend.S arch/arm/mach-mx6/mx6_wfi.S arch/arm/mach-mx6/plat_hotplug.c arch/arm/mach-mx6/pm.c arch/arm/mach-mx6/system.c arch/arm/plat-mxc/cpufreq.c arch/arm/plat-mxc/system.c drivers/media/video/mxc/capture/Kconfig drivers/media/video/mxc/capture/mxc_v4l2_capture.c drivers/media/video/mxc/capture/ov5640_mipi.c drivers/media/video/mxc/output/mxc_vout.c drivers/mmc/host/sdhci.c drivers/mtd/nand/gpmi-nand/gpmi-lib.c drivers/mxc/gpu-viv/arch/XAQ2/hal/kernel/gc_hal_kernel_hardware.c drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_mmu.c drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_enum.h drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_options.h drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_version.h drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_driver.c drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c drivers/mxc/ipu3/vdoa.c drivers/power/max8903_charger.c drivers/video/mxc/mxc_ipuv3_fb.c sound/soc/codecs/mxc_spdif.c sound/soc/imx/Makefile sound/soc/imx/imx-wm8962.c
Diffstat (limited to 'drivers')
-rw-r--r--drivers/cpufreq/cpufreq_interactive.c2
-rw-r--r--drivers/crypto/caam/Kconfig2
-rw-r--r--drivers/crypto/caam/caamalg.c1982
-rw-r--r--drivers/crypto/caam/compat.h16
-rw-r--r--drivers/crypto/caam/ctrl.c161
-rw-r--r--drivers/crypto/caam/desc_constr.h60
-rw-r--r--drivers/crypto/caam/intern.h28
-rw-r--r--drivers/crypto/caam/jr.c81
-rw-r--r--drivers/crypto/caam/regs.h16
-rw-r--r--drivers/dma/imx-sdma.c46
-rw-r--r--drivers/dma/pxp/pxp_dma.c17
-rw-r--r--drivers/dma/pxp/pxp_dma_v2.c11
-rwxr-xr-xdrivers/input/touchscreen/Kconfig12
-rwxr-xr-xdrivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/elan_ts.c387
-rwxr-xr-xdrivers/input/touchscreen/max11801_ts.c283
-rw-r--r--drivers/media/video/mxc/capture/ipu_prp_enc.c5
-rw-r--r--drivers/media/video/mxc/capture/mxc_v4l2_capture.c115
-rw-r--r--drivers/media/video/mxc/capture/mxc_v4l2_capture.h8
-rw-r--r--drivers/media/video/mxc/capture/ov3640.c3
-rw-r--r--drivers/media/video/mxc/capture/ov5640.c3
-rw-r--r--drivers/media/video/mxc/capture/ov5640_mipi.c17
-rw-r--r--drivers/media/video/mxc/capture/ov5642.c5
-rw-r--r--drivers/media/video/mxc/capture/ov8820_mipi.c6
-rw-r--r--drivers/media/video/mxc/output/mxc_vout.c22
-rw-r--r--drivers/mfd/pfuze-core.c7
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c26
-rwxr-xr-xdrivers/mmc/host/sdhci.c5
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-lib.c28
-rw-r--r--drivers/mxc/gpu-viv/arch/GC350/hal/kernel/gc_hal_kernel_hardware_vg.c4
-rw-r--r--drivers/mxc/gpu-viv/arch/XAQ2/hal/kernel/gc_hal_kernel_context.c3
-rw-r--r--drivers/mxc/gpu-viv/arch/XAQ2/hal/kernel/gc_hal_kernel_hardware.c23
-rw-r--r--drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel.c40
-rw-r--r--drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel.h10
-rw-r--r--drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_command.c2
-rw-r--r--drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_command_vg.c39
-rw-r--r--drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_debug.c6
-rw-r--r--drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_event.c41
-rw-r--r--drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_mmu.c6
-rw-r--r--drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_video_memory.c12
-rw-r--r--drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal.h6
-rw-r--r--drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_base.h9
-rw-r--r--drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_driver_vg.h20
-rw-r--r--drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_eglplatform.h56
-rw-r--r--drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_engine.h22
-rw-r--r--drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_enum.h8
-rw-r--r--drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_options.h6
-rw-r--r--drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_version.h4
-rw-r--r--drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c14
-rw-r--r--drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.h24
-rw-r--r--drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_driver.c16
-rw-r--r--drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c111
-rw-r--r--drivers/mxc/ipu3/ipu_common.c86
-rw-r--r--drivers/mxc/ipu3/ipu_device.c16
-rw-r--r--drivers/mxc/ipu3/ipu_disp.c68
-rw-r--r--drivers/mxc/ipu3/ipu_ic.c2
-rw-r--r--drivers/mxc/ipu3/ipu_prv.h13
-rw-r--r--drivers/mxc/ipu3/vdoa.c48
-rw-r--r--drivers/mxc/ipu3/vdoa.h2
-rw-r--r--drivers/net/can/flexcan.c11
-rwxr-xr-xdrivers/net/fec.c75
-rwxr-xr-xdrivers/power/Kconfig9
-rwxr-xr-xdrivers/power/Makefile1
-rwxr-xr-xdrivers/power/max8903_battery.c748
-rw-r--r--drivers/power/max8903_charger.c10
-rwxr-xr-xdrivers/power/sabresd_battery.c701
-rw-r--r--drivers/regulator/core.c2
-rw-r--r--drivers/regulator/pfuze100-regulator.c19
-rw-r--r--drivers/tty/serial/imx.c126
-rw-r--r--drivers/usb/gadget/zero.c33
-rw-r--r--drivers/video/mxc/Kconfig11
-rw-r--r--drivers/video/mxc/Makefile1
-rw-r--r--drivers/video/mxc/epdc_regs.h4
-rw-r--r--drivers/video/mxc/ldb.c11
-rw-r--r--drivers/video/mxc/mxc_edid.c10
-rw-r--r--drivers/video/mxc/mxc_elcdif_fb.c16
-rw-r--r--drivers/video/mxc/mxc_epdc_fb.c9
-rw-r--r--drivers/video/mxc/mxc_ipuv3_fb.c18
-rw-r--r--drivers/video/mxc/mxc_spdc_fb.c4168
-rw-r--r--drivers/video/mxc/mxc_spdc_fb.h352
-rw-r--r--drivers/video/mxc/mxcfb_seiko_wvga.c4
81 files changed, 9378 insertions, 1033 deletions
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index 123c86409ec1..2fc2f5f49e54 100644
--- a/drivers/cpufreq/cpufreq_interactive.c
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -827,7 +827,7 @@ int cpufreq_gov_irq_tuner_register(struct irq_tuner dbs_irq_tuner)
}
EXPORT_SYMBOL_GPL(cpufreq_gov_irq_tuner_register);
#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE
-fs_initcall(cpufreq_interactive_init);
+late_initcall(cpufreq_interactive_init);
#else
module_init(cpufreq_interactive_init);
#endif
diff --git a/drivers/crypto/caam/Kconfig b/drivers/crypto/caam/Kconfig
index 2d876bb98ff4..82a1011978d8 100644
--- a/drivers/crypto/caam/Kconfig
+++ b/drivers/crypto/caam/Kconfig
@@ -1,6 +1,6 @@
config CRYPTO_DEV_FSL_CAAM
tristate "Freescale CAAM-Multicore driver backend"
- depends on FSL_SOC
+ depends on FSL_SOC || ARCH_MXC
help
Enables the driver module for Freescale's Cryptographic Accelerator
and Assurance Module (CAAM), also known as the SEC version 4 (SEC4).
diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c
index 676d957c22b0..3c3e76b6ee1f 100644
--- a/drivers/crypto/caam/caamalg.c
+++ b/drivers/crypto/caam/caamalg.c
@@ -1,7 +1,7 @@
/*
* caam - Freescale FSL CAAM support for crypto API
*
- * Copyright 2008-2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008-2012 Freescale Semiconductor, Inc.
*
* Based on talitos crypto API driver.
*
@@ -62,10 +62,22 @@
#define CAAM_MAX_IV_LENGTH 16
/* length of descriptors text */
-#define DESC_AEAD_SHARED_TEXT_LEN 4
-#define DESC_AEAD_ENCRYPT_TEXT_LEN 21
-#define DESC_AEAD_DECRYPT_TEXT_LEN 24
-#define DESC_AEAD_GIVENCRYPT_TEXT_LEN 27
+#define DESC_JOB_IO_LEN (CAAM_CMD_SZ * 3 + CAAM_PTR_SZ * 3)
+
+#define DESC_AEAD_BASE (4 * CAAM_CMD_SZ)
+#define DESC_AEAD_ENC_LEN (DESC_AEAD_BASE + 16 * CAAM_CMD_SZ)
+#define DESC_AEAD_DEC_LEN (DESC_AEAD_BASE + 21 * CAAM_CMD_SZ)
+#define DESC_AEAD_GIVENC_LEN (DESC_AEAD_ENC_LEN + 7 * CAAM_CMD_SZ)
+
+#define DESC_ABLKCIPHER_BASE (3 * CAAM_CMD_SZ)
+#define DESC_ABLKCIPHER_ENC_LEN (DESC_ABLKCIPHER_BASE + \
+ 20 * CAAM_CMD_SZ)
+#define DESC_ABLKCIPHER_DEC_LEN (DESC_ABLKCIPHER_BASE + \
+ 15 * CAAM_CMD_SZ)
+
+#define DESC_MAX_USED_BYTES (DESC_AEAD_GIVENC_LEN + \
+ CAAM_MAX_KEY_SIZE)
+#define DESC_MAX_USED_LEN (DESC_MAX_USED_BYTES / CAAM_CMD_SZ)
#ifdef DEBUG
/* for print_hex_dumps with line references */
@@ -76,30 +88,372 @@
#define debug(format, arg...)
#endif
+/* Set DK bit in class 1 operation if shared */
+static inline void append_dec_op1(u32 *desc, u32 type)
+{
+ u32 *jump_cmd, *uncond_jump_cmd;
+
+ jump_cmd = append_jump(desc, JUMP_TEST_ALL | JUMP_COND_SHRD);
+ append_operation(desc, type | OP_ALG_AS_INITFINAL |
+ OP_ALG_DECRYPT);
+ uncond_jump_cmd = append_jump(desc, JUMP_TEST_ALL);
+ set_jump_tgt_here(desc, jump_cmd);
+ append_operation(desc, type | OP_ALG_AS_INITFINAL |
+ OP_ALG_DECRYPT | OP_ALG_AAI_DK);
+ set_jump_tgt_here(desc, uncond_jump_cmd);
+}
+
+/*
+ * Wait for completion of class 1 key loading before allowing
+ * error propagation
+ */
+static inline void append_dec_shr_done(u32 *desc)
+{
+ u32 *jump_cmd;
+
+ jump_cmd = append_jump(desc, JUMP_CLASS_CLASS1 | JUMP_TEST_ALL);
+ set_jump_tgt_here(desc, jump_cmd);
+ append_cmd(desc, SET_OK_PROP_ERRORS | CMD_LOAD);
+}
+
+/*
+ * For aead functions, read payload and write payload,
+ * both of which are specified in req->src and req->dst
+ */
+static inline void aead_append_src_dst(u32 *desc, u32 msg_type)
+{
+ append_seq_fifo_load(desc, 0, FIFOLD_CLASS_BOTH |
+ KEY_VLF | msg_type | FIFOLD_TYPE_LASTBOTH);
+ append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | KEY_VLF);
+}
+
+/*
+ * For aead encrypt and decrypt, read iv for both classes
+ */
+static inline void aead_append_ld_iv(u32 *desc, int ivsize)
+{
+ append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_BYTE_CONTEXT |
+ LDST_CLASS_1_CCB | ivsize);
+ append_move(desc, MOVE_SRC_CLASS1CTX | MOVE_DEST_CLASS2INFIFO | ivsize);
+}
+
+/*
+ * For ablkcipher encrypt and decrypt, read from req->src and
+ * write to req->dst
+ */
+static inline void ablkcipher_append_src_dst(u32 *desc)
+{
+ append_math_add(desc, VARSEQOUTLEN, SEQINLEN, REG0, CAAM_CMD_SZ); \
+ append_math_add(desc, VARSEQINLEN, SEQINLEN, REG0, CAAM_CMD_SZ); \
+ append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | \
+ KEY_VLF | FIFOLD_TYPE_MSG | FIFOLD_TYPE_LAST1); \
+ append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | KEY_VLF); \
+}
+
+/*
+ * If all data, including src (with assoc and iv) or dst (with iv only) are
+ * contiguous
+ */
+#define GIV_SRC_CONTIG 1
+#define GIV_DST_CONTIG (1 << 1)
+
/*
* per-session context
*/
struct caam_ctx {
struct device *jrdev;
- u32 *sh_desc;
- dma_addr_t shared_desc_phys;
+ u32 sh_desc_enc[DESC_MAX_USED_LEN];
+ u32 sh_desc_dec[DESC_MAX_USED_LEN];
+ u32 sh_desc_givenc[DESC_MAX_USED_LEN];
+ dma_addr_t sh_desc_enc_dma;
+ dma_addr_t sh_desc_dec_dma;
+ dma_addr_t sh_desc_givenc_dma;
u32 class1_alg_type;
u32 class2_alg_type;
u32 alg_op;
- u8 *key;
- dma_addr_t key_phys;
+ u8 key[CAAM_MAX_KEY_SIZE];
+ dma_addr_t key_dma;
unsigned int enckeylen;
unsigned int split_key_len;
unsigned int split_key_pad_len;
unsigned int authsize;
};
-static int aead_authenc_setauthsize(struct crypto_aead *authenc,
+static void append_key_aead(u32 *desc, struct caam_ctx *ctx,
+ int keys_fit_inline)
+{
+ if (keys_fit_inline) {
+ append_key_as_imm(desc, ctx->key, ctx->split_key_pad_len,
+ ctx->split_key_len, CLASS_2 |
+ KEY_DEST_MDHA_SPLIT | KEY_ENC);
+ append_key_as_imm(desc, (void *)ctx->key +
+ ctx->split_key_pad_len, ctx->enckeylen,
+ ctx->enckeylen, CLASS_1 | KEY_DEST_CLASS_REG);
+ } else {
+ append_key(desc, ctx->key_dma, ctx->split_key_len, CLASS_2 |
+ KEY_DEST_MDHA_SPLIT | KEY_ENC);
+ append_key(desc, ctx->key_dma + ctx->split_key_pad_len,
+ ctx->enckeylen, CLASS_1 | KEY_DEST_CLASS_REG);
+ }
+}
+
+static void init_sh_desc_key_aead(u32 *desc, struct caam_ctx *ctx,
+ int keys_fit_inline)
+{
+ u32 *key_jump_cmd;
+
+ init_sh_desc(desc, HDR_SHARE_WAIT);
+
+ /* Skip if already shared */
+ key_jump_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL |
+ JUMP_COND_SHRD);
+
+ append_key_aead(desc, ctx, keys_fit_inline);
+
+ set_jump_tgt_here(desc, key_jump_cmd);
+
+ /* Propagate errors from shared to job descriptor */
+ append_cmd(desc, SET_OK_PROP_ERRORS | CMD_LOAD);
+}
+
+static int aead_set_sh_desc(struct crypto_aead *aead)
+{
+ struct aead_tfm *tfm = &aead->base.crt_aead;
+ struct caam_ctx *ctx = crypto_aead_ctx(aead);
+ struct device *jrdev = ctx->jrdev;
+ bool keys_fit_inline = 0;
+ u32 *key_jump_cmd, *jump_cmd;
+ u32 geniv, moveiv;
+ u32 *desc;
+
+ if (!ctx->enckeylen || !ctx->authsize)
+ return 0;
+
+ /*
+ * Job Descriptor and Shared Descriptors
+ * must all fit into the 64-word Descriptor h/w Buffer
+ */
+ if (DESC_AEAD_ENC_LEN + DESC_JOB_IO_LEN +
+ ctx->split_key_pad_len + ctx->enckeylen <=
+ CAAM_DESC_BYTES_MAX)
+ keys_fit_inline = 1;
+
+ /* aead_encrypt shared descriptor */
+ desc = ctx->sh_desc_enc;
+
+ init_sh_desc_key_aead(desc, ctx, keys_fit_inline);
+
+ /* Class 2 operation */
+ append_operation(desc, ctx->class2_alg_type |
+ OP_ALG_AS_INITFINAL | OP_ALG_ENCRYPT);
+
+ /* cryptlen = seqoutlen - authsize */
+ append_math_sub_imm_u32(desc, REG3, SEQOUTLEN, IMM, ctx->authsize);
+
+ /* assoclen + cryptlen = seqinlen - ivsize */
+ append_math_sub_imm_u32(desc, REG2, SEQINLEN, IMM, tfm->ivsize);
+
+ /* assoclen + cryptlen = (assoclen + cryptlen) - cryptlen */
+ append_math_sub(desc, VARSEQINLEN, REG2, REG3, CAAM_CMD_SZ);
+
+ /* read assoc before reading payload */
+ append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS2 | FIFOLD_TYPE_MSG |
+ KEY_VLF);
+ aead_append_ld_iv(desc, tfm->ivsize);
+
+ /* Class 1 operation */
+ append_operation(desc, ctx->class1_alg_type |
+ OP_ALG_AS_INITFINAL | OP_ALG_ENCRYPT);
+
+ /* Read and write cryptlen bytes */
+ append_math_add(desc, VARSEQINLEN, ZERO, REG3, CAAM_CMD_SZ);
+ append_math_add(desc, VARSEQOUTLEN, ZERO, REG3, CAAM_CMD_SZ);
+ aead_append_src_dst(desc, FIFOLD_TYPE_MSG1OUT2);
+
+ /* Write ICV */
+ append_seq_store(desc, ctx->authsize, LDST_CLASS_2_CCB |
+ LDST_SRCDST_BYTE_CONTEXT);
+
+ ctx->sh_desc_enc_dma = dma_map_single(jrdev, desc,
+ desc_bytes(desc),
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(jrdev, ctx->sh_desc_enc_dma)) {
+ dev_err(jrdev, "unable to map shared descriptor\n");
+ return -ENOMEM;
+ }
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "aead enc shdesc@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, desc,
+ desc_bytes(desc), 1);
+#endif
+
+ dma_sync_single_for_cpu(jrdev, ctx->sh_desc_enc_dma, desc_bytes(desc),
+ DMA_TO_DEVICE);
+ /*
+ * Job Descriptor and Shared Descriptors
+ * must all fit into the 64-word Descriptor h/w Buffer
+ */
+ if (DESC_AEAD_DEC_LEN + DESC_JOB_IO_LEN +
+ ctx->split_key_pad_len + ctx->enckeylen <=
+ CAAM_DESC_BYTES_MAX)
+ keys_fit_inline = 1;
+
+ desc = ctx->sh_desc_dec;
+
+ /* aead_decrypt shared descriptor */
+ init_sh_desc(desc, HDR_SHARE_WAIT);
+
+ /* Skip if already shared */
+ key_jump_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL |
+ JUMP_COND_SHRD);
+
+ append_key_aead(desc, ctx, keys_fit_inline);
+
+ /* Only propagate error immediately if shared */
+ jump_cmd = append_jump(desc, JUMP_TEST_ALL);
+ set_jump_tgt_here(desc, key_jump_cmd);
+ append_cmd(desc, SET_OK_PROP_ERRORS | CMD_LOAD);
+ set_jump_tgt_here(desc, jump_cmd);
+
+ /* Class 2 operation */
+ append_operation(desc, ctx->class2_alg_type |
+ OP_ALG_AS_INITFINAL | OP_ALG_DECRYPT | OP_ALG_ICV_ON);
+
+ /* assoclen + cryptlen = seqinlen - ivsize */
+ append_math_sub_imm_u32(desc, REG3, SEQINLEN, IMM,
+ ctx->authsize + tfm->ivsize)
+ /* assoclen = (assoclen + cryptlen) - cryptlen */
+ append_math_sub(desc, REG2, SEQOUTLEN, REG0, CAAM_CMD_SZ);
+ append_math_sub(desc, VARSEQINLEN, REG3, REG2, CAAM_CMD_SZ);
+
+ /* read assoc before reading payload */
+ append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS2 | FIFOLD_TYPE_MSG |
+ KEY_VLF);
+
+ aead_append_ld_iv(desc, tfm->ivsize);
+
+ append_dec_op1(desc, ctx->class1_alg_type);
+
+ /* Read and write cryptlen bytes */
+ append_math_add(desc, VARSEQINLEN, ZERO, REG2, CAAM_CMD_SZ);
+ append_math_add(desc, VARSEQOUTLEN, ZERO, REG2, CAAM_CMD_SZ);
+ aead_append_src_dst(desc, FIFOLD_TYPE_MSG);
+
+ /* Load ICV */
+ append_seq_fifo_load(desc, ctx->authsize, FIFOLD_CLASS_CLASS2 |
+ FIFOLD_TYPE_LAST2 | FIFOLD_TYPE_ICV);
+ append_dec_shr_done(desc);
+
+ ctx->sh_desc_dec_dma = dma_map_single(jrdev, desc,
+ desc_bytes(desc),
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(jrdev, ctx->sh_desc_dec_dma)) {
+ dev_err(jrdev, "unable to map shared descriptor\n");
+ return -ENOMEM;
+ }
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "aead dec shdesc@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, desc,
+ desc_bytes(desc), 1);
+#endif
+ dma_sync_single_for_cpu(jrdev, ctx->sh_desc_dec_dma, desc_bytes(desc),
+ DMA_TO_DEVICE);
+
+ /*
+ * Job Descriptor and Shared Descriptors
+ * must all fit into the 64-word Descriptor h/w Buffer
+ */
+ if (DESC_AEAD_GIVENC_LEN + DESC_JOB_IO_LEN +
+ ctx->split_key_pad_len + ctx->enckeylen <=
+ CAAM_DESC_BYTES_MAX)
+ keys_fit_inline = 1;
+
+ /* aead_givencrypt shared descriptor */
+ desc = ctx->sh_desc_givenc;
+
+ init_sh_desc_key_aead(desc, ctx, keys_fit_inline);
+
+ /* Generate IV */
+ geniv = NFIFOENTRY_STYPE_PAD | NFIFOENTRY_DEST_DECO |
+ NFIFOENTRY_DTYPE_MSG | NFIFOENTRY_LC1 |
+ NFIFOENTRY_PTYPE_RND | (tfm->ivsize << NFIFOENTRY_DLEN_SHIFT);
+ append_load_imm_u32(desc, geniv, LDST_CLASS_IND_CCB |
+ LDST_SRCDST_WORD_INFO_FIFO | LDST_IMM);
+ append_cmd(desc, CMD_LOAD | DISABLE_AUTO_INFO_FIFO);
+ append_move(desc, MOVE_SRC_INFIFO |
+ MOVE_DEST_CLASS1CTX | (tfm->ivsize << MOVE_LEN_SHIFT));
+ append_cmd(desc, CMD_LOAD | ENABLE_AUTO_INFO_FIFO);
+
+ /* Copy IV to class 1 context */
+ append_move(desc, MOVE_SRC_CLASS1CTX |
+ MOVE_DEST_OUTFIFO | (tfm->ivsize << MOVE_LEN_SHIFT));
+
+ /* Return to encryption */
+ append_operation(desc, ctx->class2_alg_type |
+ OP_ALG_AS_INITFINAL | OP_ALG_ENCRYPT);
+
+ /* ivsize + cryptlen = seqoutlen - authsize */
+ append_math_sub_imm_u32(desc, REG3, SEQOUTLEN, IMM, ctx->authsize);
+
+ /* assoclen = seqinlen - (ivsize + cryptlen) */
+ append_math_sub(desc, VARSEQINLEN, SEQINLEN, REG3, CAAM_CMD_SZ);
+
+ /* read assoc before reading payload */
+ append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS2 | FIFOLD_TYPE_MSG |
+ KEY_VLF);
+
+ /* Copy iv from class 1 ctx to class 2 fifo*/
+ moveiv = NFIFOENTRY_STYPE_OFIFO | NFIFOENTRY_DEST_CLASS2 |
+ NFIFOENTRY_DTYPE_MSG | (tfm->ivsize << NFIFOENTRY_DLEN_SHIFT);
+ append_load_imm_u32(desc, moveiv, LDST_CLASS_IND_CCB |
+ LDST_SRCDST_WORD_INFO_FIFO | LDST_IMM);
+ append_load_imm_u32(desc, tfm->ivsize, LDST_CLASS_2_CCB |
+ LDST_SRCDST_WORD_DATASZ_REG | LDST_IMM);
+
+ /* Class 1 operation */
+ append_operation(desc, ctx->class1_alg_type |
+ OP_ALG_AS_INITFINAL | OP_ALG_ENCRYPT);
+
+ /* Will write ivsize + cryptlen */
+ append_math_add(desc, VARSEQOUTLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
+
+ /* Not need to reload iv */
+ append_seq_fifo_load(desc, tfm->ivsize,
+ FIFOLD_CLASS_SKIP);
+
+ /* Will read cryptlen */
+ append_math_add(desc, VARSEQINLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
+ aead_append_src_dst(desc, FIFOLD_TYPE_MSG1OUT2);
+
+ /* Write ICV */
+ append_seq_store(desc, ctx->authsize, LDST_CLASS_2_CCB |
+ LDST_SRCDST_BYTE_CONTEXT);
+
+ ctx->sh_desc_givenc_dma = dma_map_single(jrdev, desc,
+ desc_bytes(desc),
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(jrdev, ctx->sh_desc_givenc_dma)) {
+ dev_err(jrdev, "unable to map shared descriptor\n");
+ return -ENOMEM;
+ }
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "aead givenc shdesc@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, desc,
+ desc_bytes(desc), 1);
+#endif
+ dma_sync_single_for_cpu(jrdev, ctx->sh_desc_givenc_dma,
+ desc_bytes(desc), DMA_TO_DEVICE);
+
+ return 0;
+}
+
+static int aead_setauthsize(struct crypto_aead *authenc,
unsigned int authsize)
{
struct caam_ctx *ctx = crypto_aead_ctx(authenc);
ctx->authsize = authsize;
+ aead_set_sh_desc(authenc);
return 0;
}
@@ -117,6 +471,7 @@ static void split_key_done(struct device *dev, u32 *desc, u32 err,
#ifdef DEBUG
dev_err(dev, "%s %d: err 0x%x\n", __func__, __LINE__, err);
#endif
+
if (err) {
char tmp[CAAM_ERROR_STR_MAX];
@@ -161,6 +516,9 @@ static u32 gen_split_key(struct caam_ctx *ctx, const u8 *key_in, u32 authkeylen)
kfree(desc);
return -ENOMEM;
}
+ dma_sync_single_for_device(jrdev, dma_addr_in, authkeylen,
+ DMA_TO_DEVICE);
+
append_key(desc, dma_addr_in, authkeylen, CLASS_2 |
KEY_DEST_CLASS_REG);
@@ -211,6 +569,8 @@ static u32 gen_split_key(struct caam_ctx *ctx, const u8 *key_in, u32 authkeylen)
#endif
}
+ dma_sync_single_for_cpu(jrdev, dma_addr_out, ctx->split_key_pad_len,
+ DMA_FROM_DEVICE);
dma_unmap_single(jrdev, dma_addr_out, ctx->split_key_pad_len,
DMA_FROM_DEVICE);
dma_unmap_single(jrdev, dma_addr_in, authkeylen, DMA_TO_DEVICE);
@@ -220,73 +580,7 @@ static u32 gen_split_key(struct caam_ctx *ctx, const u8 *key_in, u32 authkeylen)
return ret;
}
-static int build_sh_desc_ipsec(struct caam_ctx *ctx)
-{
- struct device *jrdev = ctx->jrdev;
- u32 *sh_desc;
- u32 *jump_cmd;
- bool keys_fit_inline = 0;
-
- /*
- * largest Job Descriptor and its Shared Descriptor
- * must both fit into the 64-word Descriptor h/w Buffer
- */
- if ((DESC_AEAD_GIVENCRYPT_TEXT_LEN +
- DESC_AEAD_SHARED_TEXT_LEN) * CAAM_CMD_SZ +
- ctx->split_key_pad_len + ctx->enckeylen <= CAAM_DESC_BYTES_MAX)
- keys_fit_inline = 1;
-
- /* build shared descriptor for this session */
- sh_desc = kmalloc(CAAM_CMD_SZ * DESC_AEAD_SHARED_TEXT_LEN +
- (keys_fit_inline ?
- ctx->split_key_pad_len + ctx->enckeylen :
- CAAM_PTR_SZ * 2), GFP_DMA | GFP_KERNEL);
- if (!sh_desc) {
- dev_err(jrdev, "could not allocate shared descriptor\n");
- return -ENOMEM;
- }
-
- init_sh_desc(sh_desc, HDR_SAVECTX | HDR_SHARE_SERIAL);
-
- jump_cmd = append_jump(sh_desc, CLASS_BOTH | JUMP_TEST_ALL |
- JUMP_COND_SHRD | JUMP_COND_SELF);
-
- /*
- * process keys, starting with class 2/authentication.
- */
- if (keys_fit_inline) {
- append_key_as_imm(sh_desc, ctx->key, ctx->split_key_pad_len,
- ctx->split_key_len,
- CLASS_2 | KEY_DEST_MDHA_SPLIT | KEY_ENC);
-
- append_key_as_imm(sh_desc, (void *)ctx->key +
- ctx->split_key_pad_len, ctx->enckeylen,
- ctx->enckeylen, CLASS_1 | KEY_DEST_CLASS_REG);
- } else {
- append_key(sh_desc, ctx->key_phys, ctx->split_key_len, CLASS_2 |
- KEY_DEST_MDHA_SPLIT | KEY_ENC);
- append_key(sh_desc, ctx->key_phys + ctx->split_key_pad_len,
- ctx->enckeylen, CLASS_1 | KEY_DEST_CLASS_REG);
- }
-
- /* update jump cmd now that we are at the jump target */
- set_jump_tgt_here(sh_desc, jump_cmd);
-
- ctx->shared_desc_phys = dma_map_single(jrdev, sh_desc,
- desc_bytes(sh_desc),
- DMA_TO_DEVICE);
- if (dma_mapping_error(jrdev, ctx->shared_desc_phys)) {
- dev_err(jrdev, "unable to map shared descriptor\n");
- kfree(sh_desc);
- return -ENOMEM;
- }
-
- ctx->sh_desc = sh_desc;
-
- return 0;
-}
-
-static int aead_authenc_setkey(struct crypto_aead *aead,
+static int aead_setkey(struct crypto_aead *aead,
const u8 *key, unsigned int keylen)
{
/* Sizes for MDHA pads (*not* keys): MD5, SHA1, 224, 256, 384, 512 */
@@ -326,27 +620,19 @@ static int aead_authenc_setkey(struct crypto_aead *aead,
print_hex_dump(KERN_ERR, "key in @"xstr(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
#endif
- ctx->key = kmalloc(ctx->split_key_pad_len + enckeylen,
- GFP_KERNEL | GFP_DMA);
- if (!ctx->key) {
- dev_err(jrdev, "could not allocate key output memory\n");
- return -ENOMEM;
- }
ret = gen_split_key(ctx, key, authkeylen);
if (ret) {
- kfree(ctx->key);
goto badkey;
}
/* postpend encryption key to auth split key */
memcpy(ctx->key + ctx->split_key_pad_len, key + authkeylen, enckeylen);
- ctx->key_phys = dma_map_single(jrdev, ctx->key, ctx->split_key_pad_len +
+ ctx->key_dma = dma_map_single(jrdev, ctx->key, ctx->split_key_pad_len +
enckeylen, DMA_TO_DEVICE);
- if (dma_mapping_error(jrdev, ctx->key_phys)) {
+ if (dma_mapping_error(jrdev, ctx->key_dma)) {
dev_err(jrdev, "unable to map key i/o memory\n");
- kfree(ctx->key);
return -ENOMEM;
}
#ifdef DEBUG
@@ -354,14 +640,16 @@ static int aead_authenc_setkey(struct crypto_aead *aead,
DUMP_PREFIX_ADDRESS, 16, 4, ctx->key,
ctx->split_key_pad_len + enckeylen, 1);
#endif
+ dma_sync_single_for_device(jrdev, ctx->key_dma,
+ ctx->split_key_pad_len + enckeylen,
+ DMA_TO_DEVICE);
ctx->enckeylen = enckeylen;
- ret = build_sh_desc_ipsec(ctx);
+ ret = aead_set_sh_desc(aead);
if (ret) {
- dma_unmap_single(jrdev, ctx->key_phys, ctx->split_key_pad_len +
+ dma_unmap_single(jrdev, ctx->key_dma, ctx->split_key_pad_len +
enckeylen, DMA_TO_DEVICE);
- kfree(ctx->key);
}
return ret;
@@ -370,73 +658,257 @@ badkey:
return -EINVAL;
}
+static int ablkcipher_setkey(struct crypto_ablkcipher *ablkcipher,
+ const u8 *key, unsigned int keylen)
+{
+ struct caam_ctx *ctx = crypto_ablkcipher_ctx(ablkcipher);
+ struct ablkcipher_tfm *tfm = &ablkcipher->base.crt_ablkcipher;
+ struct device *jrdev = ctx->jrdev;
+ int ret = 0;
+ u32 *key_jump_cmd, *jump_cmd;
+ u32 *desc;
+
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "key in @"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
+#endif
+
+ memcpy(ctx->key, key, keylen);
+ ctx->key_dma = dma_map_single(jrdev, ctx->key, keylen,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(jrdev, ctx->key_dma)) {
+ dev_err(jrdev, "unable to map key i/o memory\n");
+ return -ENOMEM;
+ }
+ ctx->enckeylen = keylen;
+ dma_sync_single_for_device(jrdev, ctx->key_dma, keylen, DMA_TO_DEVICE);
+
+ /* ablkcipher_encrypt shared descriptor */
+ desc = ctx->sh_desc_enc;
+ init_sh_desc(desc, HDR_SHARE_WAIT);
+ /* Skip if already shared */
+ key_jump_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL |
+ JUMP_COND_SHRD);
+
+ /* Load class1 key only */
+ append_key_as_imm(desc, (void *)ctx->key, ctx->enckeylen,
+ ctx->enckeylen, CLASS_1 |
+ KEY_DEST_CLASS_REG);
+
+ set_jump_tgt_here(desc, key_jump_cmd);
+
+ /* Propagate errors from shared to job descriptor */
+ append_cmd(desc, SET_OK_PROP_ERRORS | CMD_LOAD);
+
+ /* Load iv */
+ append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_BYTE_CONTEXT |
+ LDST_CLASS_1_CCB | tfm->ivsize);
+
+ /* Load operation */
+ append_operation(desc, ctx->class1_alg_type |
+ OP_ALG_AS_INITFINAL | OP_ALG_ENCRYPT);
+
+ /* Perform operation */
+ ablkcipher_append_src_dst(desc);
+
+ ctx->sh_desc_enc_dma = dma_map_single(jrdev, desc,
+ desc_bytes(desc),
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(jrdev, ctx->sh_desc_enc_dma)) {
+ dev_err(jrdev, "unable to map shared descriptor\n");
+ return -ENOMEM;
+ }
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "ablkcipher enc shdesc@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, desc,
+ desc_bytes(desc), 1);
+#endif
+ dma_sync_single_for_device(jrdev, ctx->sh_desc_enc_dma,
+ desc_bytes(desc), DMA_TO_DEVICE);
+
+ /* ablkcipher_decrypt shared descriptor */
+ desc = ctx->sh_desc_dec;
+
+ init_sh_desc(desc, HDR_SHARE_WAIT);
+ /* Skip if already shared */
+ key_jump_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL |
+ JUMP_COND_SHRD);
+
+ /* Load class1 key only */
+ append_key_as_imm(desc, (void *)ctx->key, ctx->enckeylen,
+ ctx->enckeylen, CLASS_1 |
+ KEY_DEST_CLASS_REG);
+
+ /* For aead, only propagate error immediately if shared */
+ jump_cmd = append_jump(desc, JUMP_TEST_ALL);
+ set_jump_tgt_here(desc, key_jump_cmd);
+ append_cmd(desc, SET_OK_PROP_ERRORS | CMD_LOAD);
+ set_jump_tgt_here(desc, jump_cmd);
+
+ /* load IV */
+ append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_BYTE_CONTEXT |
+ LDST_CLASS_1_CCB | tfm->ivsize);
+
+ /* Choose operation */
+ append_dec_op1(desc, ctx->class1_alg_type);
+
+ /* Perform operation */
+ ablkcipher_append_src_dst(desc);
+
+ /* Wait for key to load before allowing propagating error */
+ append_dec_shr_done(desc);
+
+ ctx->sh_desc_dec_dma = dma_map_single(jrdev, desc,
+ desc_bytes(desc),
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(jrdev, ctx->sh_desc_enc_dma)) {
+ dev_err(jrdev, "unable to map shared descriptor\n");
+ return -ENOMEM;
+ }
+
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "ablkcipher dec shdesc@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, desc,
+ desc_bytes(desc), 1);
+#endif
+ dma_sync_single_for_device(jrdev, ctx->sh_desc_dec_dma,
+ desc_bytes(desc), DMA_TO_DEVICE);
+
+ return ret;
+}
+
+/*
+ * 16-byte hardware scatter/gather table
+ * An 8-byte table exists in the hardware spec, but has never been
+ * implemented to date. The 8/16 option is selected at RTL-compile-time.
+ * and this selection is visible in the Compile Time Parameters Register
+ */
+
+#define LINKTBL_EXT 0x80000000 /* Entry points to table */
+#define LINKTBL_FINAL 0x40000000 /* Last ent in table */
+#define LINKTBL_BPID_MASK 0x000000ff
+#define LINKTBL_BPID_SHIFT 16
+#define LINKTBL_LEN_MASK 0x3fffffff /* Excludes EXT and FINAL */
+#define LINKTBL_OFFS_MASK 0x00001fff
+
struct link_tbl_entry {
+#ifdef CONFIG_64BIT
u64 ptr;
+#else
+ u32 reserved;
+ u32 ptr;
+#endif
u32 len;
- u8 reserved;
- u8 buf_pool_id;
- u16 offset;
+ u32 bpid_offset; /* BPID in high, offset in lowest bits */
};
/*
- * ipsec_esp_edesc - s/w-extended ipsec_esp descriptor
+ * aead_edesc - s/w-extended aead descriptor
+ * @assoc_nents: number of segments in associated data (SPI+Seq) scatterlist
* @src_nents: number of segments in input scatterlist
* @dst_nents: number of segments in output scatterlist
- * @assoc_nents: number of segments in associated data (SPI+Seq) scatterlist
+ * @iv_dma: dma address of iv for checking continuity and link table
* @desc: h/w descriptor (variable length; must not exceed MAX_CAAM_DESCSIZE)
* @link_tbl_bytes: length of dma mapped link_tbl space
* @link_tbl_dma: bus physical mapped address of h/w link table
* @hw_desc: the h/w job descriptor followed by any referenced link tables
*/
-struct ipsec_esp_edesc {
+struct aead_edesc {
int assoc_nents;
int src_nents;
int dst_nents;
+ dma_addr_t iv_dma;
int link_tbl_bytes;
dma_addr_t link_tbl_dma;
struct link_tbl_entry *link_tbl;
u32 hw_desc[0];
};
-static void ipsec_esp_unmap(struct device *dev,
- struct ipsec_esp_edesc *edesc,
- struct aead_request *areq)
-{
- dma_unmap_sg(dev, areq->assoc, edesc->assoc_nents, DMA_TO_DEVICE);
+/*
+ * ablkcipher_edesc - s/w-extended ablkcipher descriptor
+ * @src_nents: number of segments in input scatterlist
+ * @dst_nents: number of segments in output scatterlist
+ * @iv_dma: dma address of iv for checking continuity and link table
+ * @desc: h/w descriptor (variable length; must not exceed MAX_CAAM_DESCSIZE)
+ * @link_tbl_bytes: length of dma mapped link_tbl space
+ * @link_tbl_dma: bus physical mapped address of h/w link table
+ * @hw_desc: the h/w job descriptor followed by any referenced link tables
+ */
+struct ablkcipher_edesc {
+ int src_nents;
+ int dst_nents;
+ dma_addr_t iv_dma;
+ int link_tbl_bytes;
+ dma_addr_t link_tbl_dma;
+ struct link_tbl_entry *link_tbl;
+ u32 hw_desc[0];
+};
- if (unlikely(areq->dst != areq->src)) {
- dma_unmap_sg(dev, areq->src, edesc->src_nents,
- DMA_TO_DEVICE);
- dma_unmap_sg(dev, areq->dst, edesc->dst_nents,
- DMA_FROM_DEVICE);
+static void caam_unmap(struct device *dev, struct scatterlist *src,
+ struct scatterlist *dst, int src_nents, int dst_nents,
+ dma_addr_t iv_dma, int ivsize, dma_addr_t link_tbl_dma,
+ int link_tbl_bytes)
+{
+ if (unlikely(dst != src)) {
+ dma_unmap_sg(dev, src, src_nents, DMA_TO_DEVICE);
+ dma_sync_sg_for_cpu(dev, dst, dst_nents, DMA_FROM_DEVICE);
+ dma_unmap_sg(dev, dst, dst_nents, DMA_FROM_DEVICE);
} else {
- dma_unmap_sg(dev, areq->src, edesc->src_nents,
- DMA_BIDIRECTIONAL);
+ dma_sync_sg_for_cpu(dev, src, src_nents, DMA_BIDIRECTIONAL);
+ dma_unmap_sg(dev, src, src_nents, DMA_BIDIRECTIONAL);
}
- if (edesc->link_tbl_bytes)
- dma_unmap_single(dev, edesc->link_tbl_dma,
- edesc->link_tbl_bytes,
+ if (iv_dma)
+ dma_unmap_single(dev, iv_dma, ivsize, DMA_TO_DEVICE);
+
+ if (link_tbl_bytes)
+ dma_unmap_single(dev, link_tbl_dma, link_tbl_bytes,
DMA_TO_DEVICE);
}
-/*
- * ipsec_esp descriptor callbacks
- */
-static void ipsec_esp_encrypt_done(struct device *jrdev, u32 *desc, u32 err,
+static void aead_unmap(struct device *dev,
+ struct aead_edesc *edesc,
+ struct aead_request *req)
+{
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
+ int ivsize = crypto_aead_ivsize(aead);
+
+ dma_unmap_sg(dev, req->assoc, edesc->assoc_nents, DMA_TO_DEVICE);
+
+ caam_unmap(dev, req->src, req->dst,
+ edesc->src_nents, edesc->dst_nents,
+ edesc->iv_dma, ivsize, edesc->link_tbl_dma,
+ edesc->link_tbl_bytes);
+}
+
+static void ablkcipher_unmap(struct device *dev,
+ struct ablkcipher_edesc *edesc,
+ struct ablkcipher_request *req)
+{
+ struct crypto_ablkcipher *ablkcipher = crypto_ablkcipher_reqtfm(req);
+ int ivsize = crypto_ablkcipher_ivsize(ablkcipher);
+
+ caam_unmap(dev, req->src, req->dst,
+ edesc->src_nents, edesc->dst_nents,
+ edesc->iv_dma, ivsize, edesc->link_tbl_dma,
+ edesc->link_tbl_bytes);
+}
+
+static void aead_encrypt_done(struct device *jrdev, u32 *desc, u32 err,
void *context)
{
- struct aead_request *areq = context;
- struct ipsec_esp_edesc *edesc;
+ struct aead_request *req = context;
+ struct aead_edesc *edesc;
#ifdef DEBUG
- struct crypto_aead *aead = crypto_aead_reqtfm(areq);
- int ivsize = crypto_aead_ivsize(aead);
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct caam_ctx *ctx = crypto_aead_ctx(aead);
+ int ivsize = crypto_aead_ivsize(aead);
dev_err(jrdev, "%s %d: err 0x%x\n", __func__, __LINE__, err);
#endif
- edesc = (struct ipsec_esp_edesc *)((char *)desc -
- offsetof(struct ipsec_esp_edesc, hw_desc));
+
+ edesc = (struct aead_edesc *)((char *)desc -
+ offsetof(struct aead_edesc, hw_desc));
if (err) {
char tmp[CAAM_ERROR_STR_MAX];
@@ -444,39 +916,50 @@ static void ipsec_esp_encrypt_done(struct device *jrdev, u32 *desc, u32 err,
dev_err(jrdev, "%08x: %s\n", err, caam_jr_strstatus(tmp, err));
}
- ipsec_esp_unmap(jrdev, edesc, areq);
+ aead_unmap(jrdev, edesc, req);
#ifdef DEBUG
print_hex_dump(KERN_ERR, "assoc @"xstr(__LINE__)": ",
- DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(areq->assoc),
- areq->assoclen , 1);
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->assoc),
+ req->assoclen , 1);
print_hex_dump(KERN_ERR, "dstiv @"xstr(__LINE__)": ",
- DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(areq->src) - ivsize,
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->src) - ivsize,
edesc->src_nents ? 100 : ivsize, 1);
print_hex_dump(KERN_ERR, "dst @"xstr(__LINE__)": ",
- DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(areq->src),
- edesc->src_nents ? 100 : areq->cryptlen +
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->src),
+ edesc->src_nents ? 100 : req->cryptlen +
ctx->authsize + 4, 1);
#endif
kfree(edesc);
- aead_request_complete(areq, err);
+ aead_request_complete(req, err);
}
-static void ipsec_esp_decrypt_done(struct device *jrdev, u32 *desc, u32 err,
+static void aead_decrypt_done(struct device *jrdev, u32 *desc, u32 err,
void *context)
{
- struct aead_request *areq = context;
- struct ipsec_esp_edesc *edesc;
+ struct aead_request *req = context;
+ struct aead_edesc *edesc;
#ifdef DEBUG
- struct crypto_aead *aead = crypto_aead_reqtfm(areq);
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct caam_ctx *ctx = crypto_aead_ctx(aead);
+ int ivsize = crypto_aead_ivsize(aead);
dev_err(jrdev, "%s %d: err 0x%x\n", __func__, __LINE__, err);
#endif
- edesc = (struct ipsec_esp_edesc *)((char *)desc -
- offsetof(struct ipsec_esp_edesc, hw_desc));
+
+ edesc = (struct aead_edesc *)((char *)desc -
+ offsetof(struct aead_edesc, hw_desc));
+
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "dstiv @"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, req->iv,
+ ivsize, 1);
+ print_hex_dump(KERN_ERR, "dst @"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->dst),
+ req->cryptlen, 1);
+#endif
if (err) {
char tmp[CAAM_ERROR_STR_MAX];
@@ -484,7 +967,7 @@ static void ipsec_esp_decrypt_done(struct device *jrdev, u32 *desc, u32 err,
dev_err(jrdev, "%08x: %s\n", err, caam_jr_strstatus(tmp, err));
}
- ipsec_esp_unmap(jrdev, edesc, areq);
+ aead_unmap(jrdev, edesc, req);
/*
* verify hw auth check passed else return -EBADMSG
@@ -495,255 +978,423 @@ static void ipsec_esp_decrypt_done(struct device *jrdev, u32 *desc, u32 err,
#ifdef DEBUG
print_hex_dump(KERN_ERR, "iphdrout@"xstr(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4,
- ((char *)sg_virt(areq->assoc) - sizeof(struct iphdr)),
- sizeof(struct iphdr) + areq->assoclen +
- ((areq->cryptlen > 1500) ? 1500 : areq->cryptlen) +
+ ((char *)sg_virt(req->assoc) - sizeof(struct iphdr)),
+ sizeof(struct iphdr) + req->assoclen +
+ ((req->cryptlen > 1500) ? 1500 : req->cryptlen) +
ctx->authsize + 36, 1);
if (!err && edesc->link_tbl_bytes) {
- struct scatterlist *sg = sg_last(areq->src, edesc->src_nents);
+ struct scatterlist *sg = sg_last(req->src, edesc->src_nents);
print_hex_dump(KERN_ERR, "sglastout@"xstr(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(sg),
sg->length + ctx->authsize + 16, 1);
}
#endif
+
kfree(edesc);
- aead_request_complete(areq, err);
+ aead_request_complete(req, err);
+}
+
+static void ablkcipher_encrypt_done(struct device *jrdev, u32 *desc, u32 err,
+ void *context)
+{
+ struct ablkcipher_request *req = context;
+ struct ablkcipher_edesc *edesc;
+#ifdef DEBUG
+ struct crypto_ablkcipher *ablkcipher = crypto_ablkcipher_reqtfm(req);
+ int ivsize = crypto_ablkcipher_ivsize(ablkcipher);
+
+ dev_err(jrdev, "%s %d: err 0x%x\n", __func__, __LINE__, err);
+#endif
+
+ edesc = (struct ablkcipher_edesc *)((char *)desc -
+ offsetof(struct ablkcipher_edesc, hw_desc));
+
+ if (err) {
+ char tmp[CAAM_ERROR_STR_MAX];
+
+ dev_err(jrdev, "%08x: %s\n", err, caam_jr_strstatus(tmp, err));
+ }
+
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "dstiv @"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, req->info,
+ edesc->src_nents > 1 ? 100 : ivsize, 1);
+ print_hex_dump(KERN_ERR, "dst @"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->src),
+ edesc->dst_nents > 1 ? 100 : req->nbytes, 1);
+#endif
+
+ ablkcipher_unmap(jrdev, edesc, req);
+ kfree(edesc);
+
+ ablkcipher_request_complete(req, err);
+}
+
+static void ablkcipher_decrypt_done(struct device *jrdev, u32 *desc, u32 err,
+ void *context)
+{
+ struct ablkcipher_request *req = context;
+ struct ablkcipher_edesc *edesc;
+#ifdef DEBUG
+ struct crypto_ablkcipher *ablkcipher = crypto_ablkcipher_reqtfm(req);
+ int ivsize = crypto_ablkcipher_ivsize(ablkcipher);
+
+ dev_err(jrdev, "%s %d: err 0x%x\n", __func__, __LINE__, err);
+#endif
+
+ edesc = (struct ablkcipher_edesc *)((char *)desc -
+ offsetof(struct ablkcipher_edesc, hw_desc));
+ if (err) {
+ char tmp[CAAM_ERROR_STR_MAX];
+
+ dev_err(jrdev, "%08x: %s\n", err, caam_jr_strstatus(tmp, err));
+ }
+
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "dstiv @"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, req->info,
+ ivsize, 1);
+ print_hex_dump(KERN_ERR, "dst @"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->src),
+ edesc->dst_nents > 1 ? 100 : req->nbytes, 1);
+#endif
+
+ ablkcipher_unmap(jrdev, edesc, req);
+ kfree(edesc);
+
+ ablkcipher_request_complete(req, err);
+}
+
+static void sg_to_link_tbl_one(struct link_tbl_entry *link_tbl_ptr,
+ dma_addr_t dma, u32 len, u32 offset)
+{
+#ifndef CONFIG_64BIT
+ link_tbl_ptr->reserved = 0; /* ensure high half is zero */
+#endif
+ link_tbl_ptr->ptr = dma;
+ link_tbl_ptr->len = (len & LINKTBL_LEN_MASK);
+ /* Does not add in buffer pool ID's at this time */
+ link_tbl_ptr->bpid_offset = (offset & LINKTBL_OFFS_MASK);
+
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "link_tbl_ptr@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, link_tbl_ptr,
+ sizeof(struct link_tbl_entry), 1);
+#endif
}
/*
* convert scatterlist to h/w link table format
- * scatterlist must have been previously dma mapped
+ * but does not have final bit; instead, returns last entry
*/
-static void sg_to_link_tbl(struct scatterlist *sg, int sg_count,
- struct link_tbl_entry *link_tbl_ptr, u32 offset)
+static struct link_tbl_entry *sg_to_link_tbl(struct scatterlist *sg,
+ int sg_count, struct link_tbl_entry
+ *link_tbl_ptr, u32 offset)
{
while (sg_count) {
- link_tbl_ptr->ptr = sg_dma_address(sg);
- link_tbl_ptr->len = sg_dma_len(sg);
- link_tbl_ptr->reserved = 0;
- link_tbl_ptr->buf_pool_id = 0;
- link_tbl_ptr->offset = offset;
+ sg_to_link_tbl_one(link_tbl_ptr, sg_dma_address(sg),
+ sg_dma_len(sg), offset);
link_tbl_ptr++;
sg = sg_next(sg);
sg_count--;
}
+ return link_tbl_ptr - 1;
+}
- /* set Final bit (marks end of link table) */
- link_tbl_ptr--;
- link_tbl_ptr->len |= 0x40000000;
+/*
+ * convert scatterlist to h/w link table format
+ * scatterlist must have been previously dma mapped
+ */
+static void sg_to_link_tbl_last(struct scatterlist *sg, int sg_count,
+ struct link_tbl_entry *link_tbl_ptr, u32 offset)
+{
+ link_tbl_ptr = sg_to_link_tbl(sg, sg_count, link_tbl_ptr, offset);
+ link_tbl_ptr->len |= LINKTBL_FINAL;
}
/*
- * fill in and submit ipsec_esp job descriptor
+ * Fill in aead job descriptor
*/
-static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq,
- u32 encrypt,
- void (*callback) (struct device *dev, u32 *desc,
- u32 err, void *context))
+static void init_aead_job(u32 *sh_desc, dma_addr_t ptr,
+ struct aead_edesc *edesc,
+ struct aead_request *req,
+ bool all_contig, bool encrypt)
{
- struct crypto_aead *aead = crypto_aead_reqtfm(areq);
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct caam_ctx *ctx = crypto_aead_ctx(aead);
- struct device *jrdev = ctx->jrdev;
- u32 *desc = edesc->hw_desc, options;
- int ret, sg_count, assoc_sg_count;
int ivsize = crypto_aead_ivsize(aead);
int authsize = ctx->authsize;
- dma_addr_t ptr, dst_dma, src_dma;
-#ifdef DEBUG
- u32 *sh_desc = ctx->sh_desc;
+ u32 *desc = edesc->hw_desc;
+ u32 out_options = 0, in_options;
+ dma_addr_t dst_dma, src_dma;
+ int len, link_tbl_index = 0;
+#ifdef DEBUG
debug("assoclen %d cryptlen %d authsize %d\n",
- areq->assoclen, areq->cryptlen, authsize);
+ req->assoclen, req->cryptlen, authsize);
print_hex_dump(KERN_ERR, "assoc @"xstr(__LINE__)": ",
- DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(areq->assoc),
- areq->assoclen , 1);
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->assoc),
+ req->assoclen , 1);
print_hex_dump(KERN_ERR, "presciv@"xstr(__LINE__)": ",
- DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(areq->src) - ivsize,
+ DUMP_PREFIX_ADDRESS, 16, 4, req->iv,
edesc->src_nents ? 100 : ivsize, 1);
print_hex_dump(KERN_ERR, "src @"xstr(__LINE__)": ",
- DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(areq->src),
- edesc->src_nents ? 100 : areq->cryptlen + authsize, 1);
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->src),
+ edesc->src_nents ? 100 : req->cryptlen, 1);
print_hex_dump(KERN_ERR, "shrdesc@"xstr(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, sh_desc,
desc_bytes(sh_desc), 1);
#endif
- assoc_sg_count = dma_map_sg(jrdev, areq->assoc, edesc->assoc_nents ?: 1,
- DMA_TO_DEVICE);
- if (areq->src == areq->dst)
- sg_count = dma_map_sg(jrdev, areq->src, edesc->src_nents ? : 1,
- DMA_BIDIRECTIONAL);
- else
- sg_count = dma_map_sg(jrdev, areq->src, edesc->src_nents ? : 1,
- DMA_TO_DEVICE);
- /* start auth operation */
- append_operation(desc, ctx->class2_alg_type | OP_ALG_AS_INITFINAL |
- (encrypt ? : OP_ALG_ICV_ON));
+ len = desc_len(sh_desc);
+ init_job_desc_shared(desc, ptr, len, HDR_SHARE_DEFER | HDR_REVERSE);
- /* Load FIFO with data for Class 2 CHA */
- options = FIFOLD_CLASS_CLASS2 | FIFOLD_TYPE_MSG;
- if (!edesc->assoc_nents) {
- ptr = sg_dma_address(areq->assoc);
+ if (all_contig) {
+ src_dma = sg_dma_address(req->assoc);
+ in_options = 0;
} else {
- sg_to_link_tbl(areq->assoc, edesc->assoc_nents,
- edesc->link_tbl, 0);
- ptr = edesc->link_tbl_dma;
- options |= LDST_SGF;
+ src_dma = edesc->link_tbl_dma;
+ link_tbl_index += (edesc->assoc_nents ? : 1) + 1 +
+ (edesc->src_nents ? : 1);
+ in_options = LDST_SGF;
}
- append_fifo_load(desc, ptr, areq->assoclen, options);
-
- /* copy iv from cipher/class1 input context to class2 infifo */
- append_move(desc, MOVE_SRC_CLASS1CTX | MOVE_DEST_CLASS2INFIFO | ivsize);
-
- if (!encrypt) {
- u32 *jump_cmd, *uncond_jump_cmd;
-
- /* JUMP if shared */
- jump_cmd = append_jump(desc, JUMP_TEST_ALL | JUMP_COND_SHRD);
+ if (encrypt)
+ append_seq_in_ptr(desc, src_dma, req->assoclen + ivsize +
+ req->cryptlen - authsize, in_options);
+ else
+ append_seq_in_ptr(desc, src_dma, req->assoclen + ivsize +
+ req->cryptlen, in_options);
- /* start class 1 (cipher) operation, non-shared version */
- append_operation(desc, ctx->class1_alg_type |
- OP_ALG_AS_INITFINAL);
+ if (likely(req->src == req->dst)) {
+ if (all_contig) {
+ dst_dma = sg_dma_address(req->src);
+ } else {
+ dst_dma = src_dma + sizeof(struct link_tbl_entry) *
+ ((edesc->assoc_nents ? : 1) + 1);
+ out_options = LDST_SGF;
+ }
+ } else {
+ if (!edesc->dst_nents) {
+ dst_dma = sg_dma_address(req->dst);
+ } else {
+ dst_dma = edesc->link_tbl_dma +
+ link_tbl_index *
+ sizeof(struct link_tbl_entry);
+ out_options = LDST_SGF;
+ }
+ }
+ if (encrypt)
+ append_seq_out_ptr(desc, dst_dma, req->cryptlen, out_options);
+ else
+ append_seq_out_ptr(desc, dst_dma, req->cryptlen - authsize,
+ out_options);
+}
- uncond_jump_cmd = append_jump(desc, 0);
+/*
+ * Fill in aead givencrypt job descriptor
+ */
+static void init_aead_giv_job(u32 *sh_desc, dma_addr_t ptr,
+ struct aead_edesc *edesc,
+ struct aead_request *req,
+ int contig)
+{
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
+ struct caam_ctx *ctx = crypto_aead_ctx(aead);
+ int ivsize = crypto_aead_ivsize(aead);
+ int authsize = ctx->authsize;
+ u32 *desc = edesc->hw_desc;
+ u32 out_options = 0, in_options;
+ dma_addr_t dst_dma, src_dma;
+ int len, link_tbl_index = 0;
- set_jump_tgt_here(desc, jump_cmd);
+#ifdef DEBUG
+ debug("assoclen %d cryptlen %d authsize %d\n",
+ req->assoclen, req->cryptlen, authsize);
+ print_hex_dump(KERN_ERR, "assoc @"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->assoc),
+ req->assoclen , 1);
+ print_hex_dump(KERN_ERR, "presciv@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, req->iv, ivsize, 1);
+ print_hex_dump(KERN_ERR, "src @"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->src),
+ edesc->src_nents > 1 ? 100 : req->cryptlen, 1);
+ print_hex_dump(KERN_ERR, "shrdesc@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, sh_desc,
+ desc_bytes(sh_desc), 1);
+#endif
- /* start class 1 (cipher) operation, shared version */
- append_operation(desc, ctx->class1_alg_type |
- OP_ALG_AS_INITFINAL | OP_ALG_AAI_DK);
- set_jump_tgt_here(desc, uncond_jump_cmd);
- } else
- append_operation(desc, ctx->class1_alg_type |
- OP_ALG_AS_INITFINAL | encrypt);
+ len = desc_len(sh_desc);
+ init_job_desc_shared(desc, ptr, len, HDR_SHARE_DEFER | HDR_REVERSE);
- /* load payload & instruct to class2 to snoop class 1 if encrypting */
- options = 0;
- if (!edesc->src_nents) {
- src_dma = sg_dma_address(areq->src);
+ if (contig & GIV_SRC_CONTIG) {
+ src_dma = sg_dma_address(req->assoc);
+ in_options = 0;
} else {
- sg_to_link_tbl(areq->src, edesc->src_nents, edesc->link_tbl +
- edesc->assoc_nents, 0);
- src_dma = edesc->link_tbl_dma + edesc->assoc_nents *
- sizeof(struct link_tbl_entry);
- options |= LDST_SGF;
+ src_dma = edesc->link_tbl_dma;
+ link_tbl_index += edesc->assoc_nents + 1 + edesc->src_nents;
+ in_options = LDST_SGF;
}
- append_seq_in_ptr(desc, src_dma, areq->cryptlen + authsize, options);
- append_seq_fifo_load(desc, areq->cryptlen, FIFOLD_CLASS_BOTH |
- FIFOLD_TYPE_LASTBOTH |
- (encrypt ? FIFOLD_TYPE_MSG1OUT2
- : FIFOLD_TYPE_MSG));
-
- /* specify destination */
- if (areq->src == areq->dst) {
- dst_dma = src_dma;
+ append_seq_in_ptr(desc, src_dma, req->assoclen + ivsize +
+ req->cryptlen - authsize, in_options);
+
+ if (contig & GIV_DST_CONTIG) {
+ dst_dma = edesc->iv_dma;
} else {
- sg_count = dma_map_sg(jrdev, areq->dst, edesc->dst_nents ? : 1,
- DMA_FROM_DEVICE);
- if (!edesc->dst_nents) {
- dst_dma = sg_dma_address(areq->dst);
- options = 0;
+ if (likely(req->src == req->dst)) {
+ dst_dma = src_dma + sizeof(struct link_tbl_entry) *
+ edesc->assoc_nents;
+ out_options = LDST_SGF;
} else {
- sg_to_link_tbl(areq->dst, edesc->dst_nents,
- edesc->link_tbl + edesc->assoc_nents +
- edesc->src_nents, 0);
- dst_dma = edesc->link_tbl_dma + (edesc->assoc_nents +
- edesc->src_nents) *
+ dst_dma = edesc->link_tbl_dma +
+ link_tbl_index *
sizeof(struct link_tbl_entry);
- options = LDST_SGF;
+ out_options = LDST_SGF;
}
}
- append_seq_out_ptr(desc, dst_dma, areq->cryptlen + authsize, options);
- append_seq_fifo_store(desc, areq->cryptlen, FIFOST_TYPE_MESSAGE_DATA);
- /* ICV */
- if (encrypt)
- append_seq_store(desc, authsize, LDST_CLASS_2_CCB |
- LDST_SRCDST_BYTE_CONTEXT);
- else
- append_seq_fifo_load(desc, authsize, FIFOLD_CLASS_CLASS2 |
- FIFOLD_TYPE_LAST2 | FIFOLD_TYPE_ICV);
+ append_seq_out_ptr(desc, dst_dma, ivsize + req->cryptlen, out_options);
+}
+
+/*
+ * Fill in ablkcipher job descriptor
+ */
+static void init_ablkcipher_job(u32 *sh_desc, dma_addr_t ptr,
+ struct ablkcipher_edesc *edesc,
+ struct ablkcipher_request *req,
+ bool iv_contig)
+{
+ struct crypto_ablkcipher *ablkcipher = crypto_ablkcipher_reqtfm(req);
+ int ivsize = crypto_ablkcipher_ivsize(ablkcipher);
+ u32 *desc = edesc->hw_desc;
+ u32 out_options = 0, in_options;
+ dma_addr_t dst_dma, src_dma;
+ int len, link_tbl_index = 0;
#ifdef DEBUG
- debug("job_desc_len %d\n", desc_len(desc));
- print_hex_dump(KERN_ERR, "jobdesc@"xstr(__LINE__)": ",
- DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc) , 1);
- print_hex_dump(KERN_ERR, "jdlinkt@"xstr(__LINE__)": ",
- DUMP_PREFIX_ADDRESS, 16, 4, edesc->link_tbl,
- edesc->link_tbl_bytes, 1);
+ print_hex_dump(KERN_ERR, "presciv@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, req->info,
+ ivsize, 1);
+ print_hex_dump(KERN_ERR, "src @"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->src),
+ edesc->src_nents ? 100 : req->nbytes, 1);
#endif
- ret = caam_jr_enqueue(jrdev, desc, callback, areq);
- if (!ret)
- ret = -EINPROGRESS;
- else {
- ipsec_esp_unmap(jrdev, edesc, areq);
- kfree(edesc);
+ len = desc_len(sh_desc);
+ init_job_desc_shared(desc, ptr, len, HDR_SHARE_DEFER | HDR_REVERSE);
+
+ if (iv_contig) {
+ src_dma = edesc->iv_dma;
+ in_options = 0;
+ } else {
+ src_dma = edesc->link_tbl_dma;
+ link_tbl_index += (iv_contig ? 0 : 1) + edesc->src_nents;
+ in_options = LDST_SGF;
}
+ append_seq_in_ptr(desc, src_dma, req->nbytes + ivsize, in_options);
- return ret;
+ if (likely(req->src == req->dst)) {
+ if (!edesc->src_nents && iv_contig) {
+ dst_dma = sg_dma_address(req->src);
+ } else {
+ dst_dma = edesc->link_tbl_dma +
+ sizeof(struct link_tbl_entry);
+ out_options = LDST_SGF;
+ }
+ } else {
+ if (!edesc->dst_nents) {
+ dst_dma = sg_dma_address(req->dst);
+ } else {
+ dst_dma = edesc->link_tbl_dma +
+ link_tbl_index * sizeof(struct link_tbl_entry);
+ out_options = LDST_SGF;
+ }
+ }
+ append_seq_out_ptr(desc, dst_dma, req->nbytes, out_options);
}
/*
* derive number of elements in scatterlist
*/
-static int sg_count(struct scatterlist *sg_list, int nbytes, int *chained)
+static int sg_count(struct scatterlist *sg_list, int nbytes)
{
struct scatterlist *sg = sg_list;
int sg_nents = 0;
- *chained = 0;
while (nbytes > 0) {
sg_nents++;
nbytes -= sg->length;
if (!sg_is_last(sg) && (sg + 1)->length == 0)
- *chained = 1;
+ BUG(); /* Not support chaining */
sg = scatterwalk_sg_next(sg);
}
+ if (likely(sg_nents == 1))
+ return 0;
+
return sg_nents;
}
/*
- * allocate and map the ipsec_esp extended descriptor
+ * allocate and map the aead extended descriptor
*/
-static struct ipsec_esp_edesc *ipsec_esp_edesc_alloc(struct aead_request *areq,
- int desc_bytes)
+static struct aead_edesc *aead_edesc_alloc(struct aead_request *req,
+ int desc_bytes, bool *all_contig_ptr)
{
- struct crypto_aead *aead = crypto_aead_reqtfm(areq);
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct caam_ctx *ctx = crypto_aead_ctx(aead);
struct device *jrdev = ctx->jrdev;
- gfp_t flags = areq->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL :
- GFP_ATOMIC;
- int assoc_nents, src_nents, dst_nents = 0, chained, link_tbl_bytes;
- struct ipsec_esp_edesc *edesc;
-
- assoc_nents = sg_count(areq->assoc, areq->assoclen, &chained);
- BUG_ON(chained);
- if (likely(assoc_nents == 1))
- assoc_nents = 0;
-
- src_nents = sg_count(areq->src, areq->cryptlen + ctx->authsize,
- &chained);
- BUG_ON(chained);
- if (src_nents == 1)
- src_nents = 0;
-
- if (unlikely(areq->dst != areq->src)) {
- dst_nents = sg_count(areq->dst, areq->cryptlen + ctx->authsize,
- &chained);
- BUG_ON(chained);
- if (dst_nents == 1)
- dst_nents = 0;
+ gfp_t flags = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG |
+ CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC;
+ int assoc_nents, src_nents, dst_nents = 0;
+ struct aead_edesc *edesc;
+ dma_addr_t iv_dma = 0;
+ int sgc;
+ bool all_contig = true;
+ int ivsize = crypto_aead_ivsize(aead);
+ int link_tbl_index, link_tbl_len = 0, link_tbl_bytes;
+
+ assoc_nents = sg_count(req->assoc, req->assoclen);
+ src_nents = sg_count(req->src, req->cryptlen);
+
+ if (unlikely(req->dst != req->src))
+ dst_nents = sg_count(req->dst, req->cryptlen);
+
+ sgc = dma_map_sg(jrdev, req->assoc, assoc_nents ? : 1,
+ DMA_BIDIRECTIONAL);
+ dma_sync_sg_for_device(jrdev, req->assoc, sgc,
+ DMA_BIDIRECTIONAL);
+ if (likely(req->src == req->dst)) {
+ sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1,
+ DMA_BIDIRECTIONAL);
+ dma_sync_sg_for_device(jrdev, req->src, sgc,
+ DMA_BIDIRECTIONAL);
+ } else {
+ sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1,
+ DMA_TO_DEVICE);
+ dma_sync_sg_for_device(jrdev, req->src, sgc,
+ DMA_TO_DEVICE);
+ sgc = dma_map_sg(jrdev, req->dst, dst_nents ? : 1,
+ DMA_FROM_DEVICE);
}
- link_tbl_bytes = (assoc_nents + src_nents + dst_nents) *
- sizeof(struct link_tbl_entry);
- debug("link_tbl_bytes %d\n", link_tbl_bytes);
+ /* Check if data are contiguous */
+ iv_dma = dma_map_single(jrdev, req->iv, ivsize, DMA_TO_DEVICE);
+ if (assoc_nents || sg_dma_address(req->assoc) + req->assoclen !=
+ iv_dma || src_nents || iv_dma + ivsize !=
+ sg_dma_address(req->src)) {
+ all_contig = false;
+ assoc_nents = assoc_nents ? : 1;
+ src_nents = src_nents ? : 1;
+ link_tbl_len = assoc_nents + 1 + src_nents;
+ }
+ link_tbl_len += dst_nents;
+
+ link_tbl_bytes = link_tbl_len * sizeof(struct link_tbl_entry);
+ dma_sync_single_for_device(jrdev, iv_dma, ivsize, DMA_TO_DEVICE);
/* allocate space for base edesc and hw desc commands, link tables */
- edesc = kmalloc(sizeof(struct ipsec_esp_edesc) + desc_bytes +
+ edesc = kmalloc(sizeof(struct aead_edesc) + desc_bytes +
link_tbl_bytes, GFP_DMA | flags);
if (!edesc) {
dev_err(jrdev, "could not allocate extended descriptor\n");
@@ -753,142 +1404,470 @@ static struct ipsec_esp_edesc *ipsec_esp_edesc_alloc(struct aead_request *areq,
edesc->assoc_nents = assoc_nents;
edesc->src_nents = src_nents;
edesc->dst_nents = dst_nents;
- edesc->link_tbl = (void *)edesc + sizeof(struct ipsec_esp_edesc) +
+ edesc->iv_dma = iv_dma;
+ edesc->link_tbl_bytes = link_tbl_bytes;
+ edesc->link_tbl = (void *)edesc + sizeof(struct aead_edesc) +
desc_bytes;
edesc->link_tbl_dma = dma_map_single(jrdev, edesc->link_tbl,
link_tbl_bytes, DMA_TO_DEVICE);
- edesc->link_tbl_bytes = link_tbl_bytes;
+ *all_contig_ptr = all_contig;
+
+ link_tbl_index = 0;
+ if (!all_contig) {
+ sg_to_link_tbl(req->assoc,
+ (assoc_nents ? : 1),
+ edesc->link_tbl +
+ link_tbl_index, 0);
+ link_tbl_index += assoc_nents ? : 1;
+ sg_to_link_tbl_one(edesc->link_tbl + link_tbl_index,
+ iv_dma, ivsize, 0);
+ link_tbl_index += 1;
+ sg_to_link_tbl_last(req->src,
+ (src_nents ? : 1),
+ edesc->link_tbl +
+ link_tbl_index, 0);
+ link_tbl_index += src_nents ? : 1;
+ }
+ if (dst_nents) {
+ sg_to_link_tbl_last(req->dst, dst_nents,
+ edesc->link_tbl + link_tbl_index, 0);
+ }
+ dma_sync_single_for_device(jrdev, edesc->link_tbl_dma, link_tbl_bytes,
+ DMA_TO_DEVICE);
return edesc;
}
-static int aead_authenc_encrypt(struct aead_request *areq)
+static int aead_encrypt(struct aead_request *req)
{
- struct ipsec_esp_edesc *edesc;
- struct crypto_aead *aead = crypto_aead_reqtfm(areq);
+ struct aead_edesc *edesc;
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct caam_ctx *ctx = crypto_aead_ctx(aead);
struct device *jrdev = ctx->jrdev;
- int ivsize = crypto_aead_ivsize(aead);
+ bool all_contig;
u32 *desc;
- dma_addr_t iv_dma;
+ int ret = 0;
+
+ req->cryptlen += ctx->authsize;
/* allocate extended descriptor */
- edesc = ipsec_esp_edesc_alloc(areq, DESC_AEAD_ENCRYPT_TEXT_LEN *
- CAAM_CMD_SZ);
+ edesc = aead_edesc_alloc(req, DESC_JOB_IO_LEN *
+ CAAM_CMD_SZ, &all_contig);
if (IS_ERR(edesc))
return PTR_ERR(edesc);
- desc = edesc->hw_desc;
-
- /* insert shared descriptor pointer */
- init_job_desc_shared(desc, ctx->shared_desc_phys,
- desc_len(ctx->sh_desc), HDR_SHARE_DEFER);
-
- iv_dma = dma_map_single(jrdev, areq->iv, ivsize, DMA_TO_DEVICE);
- /* check dma error */
+ /* Create and submit job descriptor */
+ init_aead_job(ctx->sh_desc_enc, ctx->sh_desc_enc_dma, edesc, req,
+ all_contig, true);
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "aead jobdesc@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, edesc->hw_desc,
+ desc_bytes(edesc->hw_desc), 1);
+#endif
- append_load(desc, iv_dma, ivsize,
- LDST_CLASS_1_CCB | LDST_SRCDST_BYTE_CONTEXT);
+ desc = edesc->hw_desc;
+ ret = caam_jr_enqueue(jrdev, desc, aead_encrypt_done, req);
+ if (!ret) {
+ ret = -EINPROGRESS;
+ } else {
+ aead_unmap(jrdev, edesc, req);
+ kfree(edesc);
+ }
- return ipsec_esp(edesc, areq, OP_ALG_ENCRYPT, ipsec_esp_encrypt_done);
+ return ret;
}
-static int aead_authenc_decrypt(struct aead_request *req)
+static int aead_decrypt(struct aead_request *req)
{
+ struct aead_edesc *edesc;
struct crypto_aead *aead = crypto_aead_reqtfm(req);
- int ivsize = crypto_aead_ivsize(aead);
struct caam_ctx *ctx = crypto_aead_ctx(aead);
struct device *jrdev = ctx->jrdev;
- struct ipsec_esp_edesc *edesc;
+ bool all_contig;
u32 *desc;
- dma_addr_t iv_dma;
-
- req->cryptlen -= ctx->authsize;
+ int ret = 0;
/* allocate extended descriptor */
- edesc = ipsec_esp_edesc_alloc(req, DESC_AEAD_DECRYPT_TEXT_LEN *
- CAAM_CMD_SZ);
+ edesc = aead_edesc_alloc(req, DESC_JOB_IO_LEN *
+ CAAM_CMD_SZ, &all_contig);
if (IS_ERR(edesc))
return PTR_ERR(edesc);
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "dec src@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->src),
+ req->cryptlen, 1);
+#endif
+
+ /* Create and submit job descriptor*/
+ init_aead_job(ctx->sh_desc_dec,
+ ctx->sh_desc_dec_dma, edesc, req, all_contig, false);
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "aead jobdesc@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, edesc->hw_desc,
+ desc_bytes(edesc->hw_desc), 1);
+#endif
+
desc = edesc->hw_desc;
+ ret = caam_jr_enqueue(jrdev, desc, aead_decrypt_done, req);
+ if (!ret) {
+ ret = -EINPROGRESS;
+ } else {
+ aead_unmap(jrdev, edesc, req);
+ kfree(edesc);
+ }
- /* insert shared descriptor pointer */
- init_job_desc_shared(desc, ctx->shared_desc_phys,
- desc_len(ctx->sh_desc), HDR_SHARE_DEFER);
+ return ret;
+}
- iv_dma = dma_map_single(jrdev, req->iv, ivsize, DMA_TO_DEVICE);
- /* check dma error */
+/*
+ * allocate and map the aead extended descriptor for aead givencrypt
+ */
+static struct aead_edesc *aead_giv_edesc_alloc(struct aead_givcrypt_request
+ *greq, int desc_bytes,
+ u32 *contig_ptr)
+{
+ struct aead_request *req = &greq->areq;
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
+ struct caam_ctx *ctx = crypto_aead_ctx(aead);
+ struct device *jrdev = ctx->jrdev;
+ gfp_t flags = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG |
+ CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC;
+ int assoc_nents, src_nents, dst_nents = 0;
+ struct aead_edesc *edesc;
+ dma_addr_t iv_dma = 0;
+ int sgc;
+ u32 contig = GIV_SRC_CONTIG | GIV_DST_CONTIG;
+ int ivsize = crypto_aead_ivsize(aead);
+ int link_tbl_index, link_tbl_len = 0, link_tbl_bytes;
+
+ assoc_nents = sg_count(req->assoc, req->assoclen);
+ src_nents = sg_count(req->src, req->cryptlen);
+
+ if (unlikely(req->dst != req->src))
+ dst_nents = sg_count(req->dst, req->cryptlen);
+
+ sgc = dma_map_sg(jrdev, req->assoc, assoc_nents ? : 1,
+ DMA_BIDIRECTIONAL);
+ dma_sync_sg_for_device(jrdev, req->assoc, assoc_nents ? : 1,
+ DMA_BIDIRECTIONAL);
+ if (likely(req->src == req->dst)) {
+ sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1,
+ DMA_BIDIRECTIONAL);
+ dma_sync_sg_for_device(jrdev, req->src, src_nents ? : 1,
+ DMA_BIDIRECTIONAL);
+ } else {
+ sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1,
+ DMA_TO_DEVICE);
+ dma_sync_sg_for_device(jrdev, req->src, src_nents ? : 1,
+ DMA_TO_DEVICE);
+ sgc = dma_map_sg(jrdev, req->dst, dst_nents ? : 1,
+ DMA_FROM_DEVICE);
+ }
- append_load(desc, iv_dma, ivsize,
- LDST_CLASS_1_CCB | LDST_SRCDST_BYTE_CONTEXT);
+ /* Check if data are contiguous */
+ iv_dma = dma_map_single(jrdev, greq->giv, ivsize, DMA_TO_DEVICE);
+ if (assoc_nents || sg_dma_address(req->assoc) + req->assoclen !=
+ iv_dma || src_nents || iv_dma + ivsize != sg_dma_address(req->src))
+ contig &= ~GIV_SRC_CONTIG;
+ if (dst_nents || iv_dma + ivsize != sg_dma_address(req->dst))
+ contig &= ~GIV_DST_CONTIG;
+ if (unlikely(req->src != req->dst)) {
+ dst_nents = dst_nents ? : 1;
+ link_tbl_len += 1;
+ }
+ if (!(contig & GIV_SRC_CONTIG)) {
+ assoc_nents = assoc_nents ? : 1;
+ src_nents = src_nents ? : 1;
+ link_tbl_len += assoc_nents + 1 + src_nents;
+ if (likely(req->src == req->dst))
+ contig &= ~GIV_DST_CONTIG;
+ }
+ link_tbl_len += dst_nents;
+
+ link_tbl_bytes = link_tbl_len * sizeof(struct link_tbl_entry);
+
+ dma_sync_single_for_device(jrdev, iv_dma, ivsize, DMA_TO_DEVICE);
+
+ /* allocate space for base edesc and hw desc commands, link tables */
+ edesc = kmalloc(sizeof(struct aead_edesc) + desc_bytes +
+ link_tbl_bytes, GFP_DMA | flags);
+ if (!edesc) {
+ dev_err(jrdev, "could not allocate extended descriptor\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ edesc->assoc_nents = assoc_nents;
+ edesc->src_nents = src_nents;
+ edesc->dst_nents = dst_nents;
+ edesc->iv_dma = iv_dma;
+ edesc->link_tbl_bytes = link_tbl_bytes;
+ edesc->link_tbl = (void *)edesc + sizeof(struct aead_edesc) +
+ desc_bytes;
+ edesc->link_tbl_dma = dma_map_single(jrdev, edesc->link_tbl,
+ link_tbl_bytes, DMA_TO_DEVICE);
+ *contig_ptr = contig;
+
+ link_tbl_index = 0;
+ if (!(contig & GIV_SRC_CONTIG)) {
+ sg_to_link_tbl(req->assoc, assoc_nents,
+ edesc->link_tbl +
+ link_tbl_index, 0);
+ link_tbl_index += assoc_nents;
+ sg_to_link_tbl_one(edesc->link_tbl + link_tbl_index,
+ iv_dma, ivsize, 0);
+ link_tbl_index += 1;
+ sg_to_link_tbl_last(req->src, src_nents,
+ edesc->link_tbl +
+ link_tbl_index, 0);
+ link_tbl_index += src_nents;
+ }
+ if (unlikely(req->src != req->dst && !(contig & GIV_DST_CONTIG))) {
+ sg_to_link_tbl_one(edesc->link_tbl + link_tbl_index,
+ iv_dma, ivsize, 0);
+ link_tbl_index += 1;
+ sg_to_link_tbl_last(req->dst, dst_nents,
+ edesc->link_tbl + link_tbl_index, 0);
+ }
+ dma_sync_single_for_device(jrdev, edesc->link_tbl_dma, link_tbl_bytes,
+ DMA_TO_DEVICE);
- return ipsec_esp(edesc, req, !OP_ALG_ENCRYPT, ipsec_esp_decrypt_done);
+ return edesc;
}
-static int aead_authenc_givencrypt(struct aead_givcrypt_request *req)
+static int aead_givencrypt(struct aead_givcrypt_request *areq)
{
- struct aead_request *areq = &req->areq;
- struct ipsec_esp_edesc *edesc;
- struct crypto_aead *aead = crypto_aead_reqtfm(areq);
+ struct aead_request *req = &areq->areq;
+ struct aead_edesc *edesc;
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct caam_ctx *ctx = crypto_aead_ctx(aead);
struct device *jrdev = ctx->jrdev;
- int ivsize = crypto_aead_ivsize(aead);
- dma_addr_t iv_dma;
+ u32 contig;
u32 *desc;
+ int ret = 0;
- iv_dma = dma_map_single(jrdev, req->giv, ivsize, DMA_FROM_DEVICE);
-
- debug("%s: giv %p\n", __func__, req->giv);
+ req->cryptlen += ctx->authsize;
/* allocate extended descriptor */
- edesc = ipsec_esp_edesc_alloc(areq, DESC_AEAD_GIVENCRYPT_TEXT_LEN *
- CAAM_CMD_SZ);
+ edesc = aead_giv_edesc_alloc(areq, DESC_JOB_IO_LEN *
+ CAAM_CMD_SZ, &contig);
+
if (IS_ERR(edesc))
return PTR_ERR(edesc);
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "giv src@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, sg_virt(req->src),
+ req->cryptlen, 1);
+#endif
+
+ /* Create and submit job descriptor*/
+ init_aead_giv_job(ctx->sh_desc_givenc,
+ ctx->sh_desc_givenc_dma, edesc, req, contig);
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "aead jobdesc@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, edesc->hw_desc,
+ desc_bytes(edesc->hw_desc), 1);
+#endif
+
desc = edesc->hw_desc;
+ ret = caam_jr_enqueue(jrdev, desc, aead_encrypt_done, req);
+ if (!ret) {
+ ret = -EINPROGRESS;
+ } else {
+ aead_unmap(jrdev, edesc, req);
+ kfree(edesc);
+ }
- /* insert shared descriptor pointer */
- init_job_desc_shared(desc, ctx->shared_desc_phys,
- desc_len(ctx->sh_desc), HDR_SHARE_DEFER);
+ return ret;
+}
- /*
- * LOAD IMM Info FIFO
- * to DECO, Last, Padding, Random, Message, 16 bytes
- */
- append_load_imm_u32(desc, NFIFOENTRY_DEST_DECO | NFIFOENTRY_LC1 |
- NFIFOENTRY_STYPE_PAD | NFIFOENTRY_DTYPE_MSG |
- NFIFOENTRY_PTYPE_RND | ivsize,
- LDST_SRCDST_WORD_INFO_FIFO);
+/*
+ * allocate and map the ablkcipher extended descriptor for ablkcipher
+ */
+static struct ablkcipher_edesc *ablkcipher_edesc_alloc(struct ablkcipher_request
+ *req, int desc_bytes,
+ bool *iv_contig_out)
+{
+ struct crypto_ablkcipher *ablkcipher = crypto_ablkcipher_reqtfm(req);
+ struct caam_ctx *ctx = crypto_ablkcipher_ctx(ablkcipher);
+ struct device *jrdev = ctx->jrdev;
+ gfp_t flags = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG |
+ CRYPTO_TFM_REQ_MAY_SLEEP)) ?
+ GFP_KERNEL : GFP_ATOMIC;
+ int src_nents, dst_nents = 0, link_tbl_bytes;
+ struct ablkcipher_edesc *edesc;
+ dma_addr_t iv_dma = 0;
+ bool iv_contig = false;
+ int sgc;
+ int ivsize = crypto_ablkcipher_ivsize(ablkcipher);
+ int link_tbl_index;
+
+ src_nents = sg_count(req->src, req->nbytes);
+
+ if (unlikely(req->dst != req->src))
+ dst_nents = sg_count(req->dst, req->nbytes);
+
+ if (likely(req->src == req->dst)) {
+ sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1,
+ DMA_BIDIRECTIONAL);
+ dma_sync_sg_for_device(jrdev, req->src, sgc,
+ DMA_BIDIRECTIONAL);
+ } else {
+ sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1,
+ DMA_TO_DEVICE);
+ dma_sync_sg_for_device(jrdev, req->src, sgc, DMA_TO_DEVICE);
+ sgc = dma_map_sg(jrdev, req->dst, dst_nents ? : 1,
+ DMA_FROM_DEVICE);
+ }
+ /* FIXME: no test for sgc values returned above... */
/*
- * disable info fifo entries since the above serves as the entry
- * this way, the MOVE command won't generate an entry.
- * Note that this isn't required in more recent versions of
- * SEC as a MOVE that doesn't do info FIFO entries is available.
+ * Check if iv can be contiguous with source and destination.
+ * If so, include it. If not, create scatterlist.
*/
- append_cmd(desc, CMD_LOAD | DISABLE_AUTO_INFO_FIFO);
+ iv_dma = dma_map_single(jrdev, req->info, ivsize, DMA_TO_DEVICE);
+ dma_sync_single_for_device(jrdev, iv_dma, ivsize, DMA_TO_DEVICE);
+ if (!src_nents && iv_dma + ivsize == sg_dma_address(req->src))
+ iv_contig = true;
+ else
+ src_nents = src_nents ? : 1;
+ link_tbl_bytes = ((iv_contig ? 0 : 1) + src_nents + dst_nents) *
+ sizeof(struct link_tbl_entry);
- /* MOVE DECO Alignment -> C1 Context 16 bytes */
- append_move(desc, MOVE_SRC_INFIFO | MOVE_DEST_CLASS1CTX | ivsize);
+ /* allocate space for base edesc and hw desc commands, link tables */
+ edesc = kmalloc(sizeof(struct ablkcipher_edesc) + desc_bytes +
+ link_tbl_bytes, GFP_DMA | flags);
+ if (!edesc) {
+ dev_err(jrdev, "could not allocate extended descriptor\n");
+ return ERR_PTR(-ENOMEM);
+ }
- /* re-enable info fifo entries */
- append_cmd(desc, CMD_LOAD | ENABLE_AUTO_INFO_FIFO);
+ edesc->src_nents = src_nents;
+ edesc->dst_nents = dst_nents;
+ edesc->link_tbl_bytes = link_tbl_bytes;
+ edesc->link_tbl = (void *)edesc + sizeof(struct ablkcipher_edesc) +
+ desc_bytes;
+
+ link_tbl_index = 0;
+ if (!iv_contig) {
+ sg_to_link_tbl_one(edesc->link_tbl, iv_dma, ivsize, 0);
+ sg_to_link_tbl_last(req->src, src_nents,
+ edesc->link_tbl + 1, 0);
+ link_tbl_index += 1 + src_nents;
+ }
+
+ if (unlikely(dst_nents)) {
+ sg_to_link_tbl_last(req->dst, dst_nents,
+ edesc->link_tbl + link_tbl_index, 0);
+ }
+
+ edesc->link_tbl_dma = dma_map_single(jrdev, edesc->link_tbl,
+ link_tbl_bytes, DMA_TO_DEVICE);
+ edesc->iv_dma = iv_dma;
- /* MOVE C1 Context -> OFIFO 16 bytes */
- append_move(desc, MOVE_SRC_CLASS1CTX | MOVE_DEST_OUTFIFO | ivsize);
+ dma_sync_single_for_device(jrdev, edesc->link_tbl_dma, link_tbl_bytes,
+ DMA_TO_DEVICE);
- append_fifo_store(desc, iv_dma, ivsize, FIFOST_TYPE_MESSAGE_DATA);
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "ablkcipher link_tbl@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, edesc->link_tbl,
+ link_tbl_bytes, 1);
+#endif
- return ipsec_esp(edesc, areq, OP_ALG_ENCRYPT, ipsec_esp_encrypt_done);
+ *iv_contig_out = iv_contig;
+ return edesc;
}
+static int ablkcipher_encrypt(struct ablkcipher_request *req)
+{
+ struct ablkcipher_edesc *edesc;
+ struct crypto_ablkcipher *ablkcipher = crypto_ablkcipher_reqtfm(req);
+ struct caam_ctx *ctx = crypto_ablkcipher_ctx(ablkcipher);
+ struct device *jrdev = ctx->jrdev;
+ bool iv_contig;
+ u32 *desc;
+ int ret = 0;
+
+ /* allocate extended descriptor */
+ edesc = ablkcipher_edesc_alloc(req, DESC_JOB_IO_LEN *
+ CAAM_CMD_SZ, &iv_contig);
+ if (IS_ERR(edesc))
+ return PTR_ERR(edesc);
+
+ /* Create and submit job descriptor*/
+ init_ablkcipher_job(ctx->sh_desc_enc,
+ ctx->sh_desc_enc_dma, edesc, req, iv_contig);
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "ablkcipher jobdesc@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, edesc->hw_desc,
+ desc_bytes(edesc->hw_desc), 1);
+#endif
+ desc = edesc->hw_desc;
+ ret = caam_jr_enqueue(jrdev, desc, ablkcipher_encrypt_done, req);
+
+ if (!ret) {
+ ret = -EINPROGRESS;
+ } else {
+ ablkcipher_unmap(jrdev, edesc, req);
+ kfree(edesc);
+ }
+
+ return ret;
+}
+
+static int ablkcipher_decrypt(struct ablkcipher_request *req)
+{
+ struct ablkcipher_edesc *edesc;
+ struct crypto_ablkcipher *ablkcipher = crypto_ablkcipher_reqtfm(req);
+ struct caam_ctx *ctx = crypto_ablkcipher_ctx(ablkcipher);
+ struct device *jrdev = ctx->jrdev;
+ bool iv_contig;
+ u32 *desc;
+ int ret = 0;
+
+ /* allocate extended descriptor */
+ edesc = ablkcipher_edesc_alloc(req, DESC_JOB_IO_LEN *
+ CAAM_CMD_SZ, &iv_contig);
+ if (IS_ERR(edesc))
+ return PTR_ERR(edesc);
+
+ /* Create and submit job descriptor*/
+ init_ablkcipher_job(ctx->sh_desc_dec,
+ ctx->sh_desc_dec_dma, edesc, req, iv_contig);
+ desc = edesc->hw_desc;
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "ablkcipher jobdesc@"xstr(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, edesc->hw_desc,
+ desc_bytes(edesc->hw_desc), 1);
+#endif
+
+ ret = caam_jr_enqueue(jrdev, desc, ablkcipher_decrypt_done, req);
+ if (!ret) {
+ ret = -EINPROGRESS;
+ } else {
+ ablkcipher_unmap(jrdev, edesc, req);
+ kfree(edesc);
+ }
+
+ return ret;
+}
+
+#define template_aead template_u.aead
+#define template_ablkcipher template_u.ablkcipher
struct caam_alg_template {
char name[CRYPTO_MAX_ALG_NAME];
char driver_name[CRYPTO_MAX_ALG_NAME];
unsigned int blocksize;
- struct aead_alg aead;
+ u32 type;
+ union {
+ struct ablkcipher_alg ablkcipher;
+ struct aead_alg aead;
+ struct blkcipher_alg blkcipher;
+ struct cipher_alg cipher;
+ struct compress_alg compress;
+ struct rng_alg rng;
+ } template_u;
u32 class1_alg_type;
u32 class2_alg_type;
u32 alg_op;
@@ -900,12 +1879,13 @@ static struct caam_alg_template driver_algs[] = {
.name = "authenc(hmac(sha1),cbc(aes))",
.driver_name = "authenc-hmac-sha1-cbc-aes-caam",
.blocksize = AES_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
.geniv = "<built-in>",
.ivsize = AES_BLOCK_SIZE,
.maxauthsize = SHA1_DIGEST_SIZE,
@@ -918,12 +1898,13 @@ static struct caam_alg_template driver_algs[] = {
.name = "authenc(hmac(sha256),cbc(aes))",
.driver_name = "authenc-hmac-sha256-cbc-aes-caam",
.blocksize = AES_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
.geniv = "<built-in>",
.ivsize = AES_BLOCK_SIZE,
.maxauthsize = SHA256_DIGEST_SIZE,
@@ -937,12 +1918,13 @@ static struct caam_alg_template driver_algs[] = {
.name = "authenc(hmac(sha512),cbc(aes))",
.driver_name = "authenc-hmac-sha512-cbc-aes-caam",
.blocksize = AES_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
.geniv = "<built-in>",
.ivsize = AES_BLOCK_SIZE,
.maxauthsize = SHA512_DIGEST_SIZE,
@@ -956,12 +1938,13 @@ static struct caam_alg_template driver_algs[] = {
.name = "authenc(hmac(sha1),cbc(des3_ede))",
.driver_name = "authenc-hmac-sha1-cbc-des3_ede-caam",
.blocksize = DES3_EDE_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES3_EDE_BLOCK_SIZE,
.maxauthsize = SHA1_DIGEST_SIZE,
@@ -974,12 +1957,13 @@ static struct caam_alg_template driver_algs[] = {
.name = "authenc(hmac(sha256),cbc(des3_ede))",
.driver_name = "authenc-hmac-sha256-cbc-des3_ede-caam",
.blocksize = DES3_EDE_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES3_EDE_BLOCK_SIZE,
.maxauthsize = SHA256_DIGEST_SIZE,
@@ -993,12 +1977,13 @@ static struct caam_alg_template driver_algs[] = {
.name = "authenc(hmac(sha512),cbc(des3_ede))",
.driver_name = "authenc-hmac-sha512-cbc-des3_ede-caam",
.blocksize = DES3_EDE_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES3_EDE_BLOCK_SIZE,
.maxauthsize = SHA512_DIGEST_SIZE,
@@ -1012,12 +1997,13 @@ static struct caam_alg_template driver_algs[] = {
.name = "authenc(hmac(sha1),cbc(des))",
.driver_name = "authenc-hmac-sha1-cbc-des-caam",
.blocksize = DES_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES_BLOCK_SIZE,
.maxauthsize = SHA1_DIGEST_SIZE,
@@ -1030,12 +2016,13 @@ static struct caam_alg_template driver_algs[] = {
.name = "authenc(hmac(sha256),cbc(des))",
.driver_name = "authenc-hmac-sha256-cbc-des-caam",
.blocksize = DES_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES_BLOCK_SIZE,
.maxauthsize = SHA256_DIGEST_SIZE,
@@ -1049,12 +2036,13 @@ static struct caam_alg_template driver_algs[] = {
.name = "authenc(hmac(sha512),cbc(des))",
.driver_name = "authenc-hmac-sha512-cbc-des-caam",
.blocksize = DES_BLOCK_SIZE,
- .aead = {
- .setkey = aead_authenc_setkey,
- .setauthsize = aead_authenc_setauthsize,
- .encrypt = aead_authenc_encrypt,
- .decrypt = aead_authenc_decrypt,
- .givencrypt = aead_authenc_givencrypt,
+ .type = CRYPTO_ALG_TYPE_AEAD,
+ .template_aead = {
+ .setkey = aead_setkey,
+ .setauthsize = aead_setauthsize,
+ .encrypt = aead_encrypt,
+ .decrypt = aead_decrypt,
+ .givencrypt = aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES_BLOCK_SIZE,
.maxauthsize = SHA512_DIGEST_SIZE,
@@ -1064,6 +2052,55 @@ static struct caam_alg_template driver_algs[] = {
OP_ALG_AAI_HMAC_PRECOMP,
.alg_op = OP_ALG_ALGSEL_SHA512 | OP_ALG_AAI_HMAC,
},
+ /* ablkcipher descriptor */
+ {
+ .name = "cbc(aes)",
+ .driver_name = "cbc-aes-caam",
+ .blocksize = AES_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .template_ablkcipher = {
+ .setkey = ablkcipher_setkey,
+ .encrypt = ablkcipher_encrypt,
+ .decrypt = ablkcipher_decrypt,
+ .geniv = "eseqiv",
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .ivsize = AES_BLOCK_SIZE,
+ },
+ .class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_CBC,
+ },
+ {
+ .name = "cbc(des3_ede)",
+ .driver_name = "cbc-3des-caam",
+ .blocksize = DES3_EDE_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .template_ablkcipher = {
+ .setkey = ablkcipher_setkey,
+ .encrypt = ablkcipher_encrypt,
+ .decrypt = ablkcipher_decrypt,
+ .geniv = "eseqiv",
+ .min_keysize = DES3_EDE_KEY_SIZE,
+ .max_keysize = DES3_EDE_KEY_SIZE,
+ .ivsize = DES3_EDE_BLOCK_SIZE,
+ },
+ .class1_alg_type = OP_ALG_ALGSEL_3DES | OP_ALG_AAI_CBC,
+ },
+ {
+ .name = "cbc(des)",
+ .driver_name = "cbc-des-caam",
+ .blocksize = DES_BLOCK_SIZE,
+ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .template_ablkcipher = {
+ .setkey = ablkcipher_setkey,
+ .encrypt = ablkcipher_encrypt,
+ .decrypt = ablkcipher_decrypt,
+ .geniv = "eseqiv",
+ .min_keysize = DES_KEY_SIZE,
+ .max_keysize = DES_KEY_SIZE,
+ .ivsize = DES_BLOCK_SIZE,
+ },
+ .class1_alg_type = OP_ALG_ALGSEL_DES | OP_ALG_AAI_CBC,
+ }
};
struct caam_crypto_alg {
@@ -1102,38 +2139,29 @@ static void caam_cra_exit(struct crypto_tfm *tfm)
{
struct caam_ctx *ctx = crypto_tfm_ctx(tfm);
- if (!dma_mapping_error(ctx->jrdev, ctx->shared_desc_phys))
- dma_unmap_single(ctx->jrdev, ctx->shared_desc_phys,
- desc_bytes(ctx->sh_desc), DMA_TO_DEVICE);
- kfree(ctx->sh_desc);
-
- if (!dma_mapping_error(ctx->jrdev, ctx->key_phys))
- dma_unmap_single(ctx->jrdev, ctx->key_phys,
- ctx->split_key_pad_len + ctx->enckeylen,
+ if (ctx->sh_desc_enc_dma &&
+ !dma_mapping_error(ctx->jrdev, ctx->sh_desc_enc_dma))
+ dma_unmap_single(ctx->jrdev, ctx->sh_desc_enc_dma,
+ desc_bytes(ctx->sh_desc_enc), DMA_TO_DEVICE);
+ if (ctx->sh_desc_dec_dma &&
+ !dma_mapping_error(ctx->jrdev, ctx->sh_desc_dec_dma))
+ dma_unmap_single(ctx->jrdev, ctx->sh_desc_dec_dma,
+ desc_bytes(ctx->sh_desc_dec), DMA_TO_DEVICE);
+ if (ctx->sh_desc_givenc_dma &&
+ !dma_mapping_error(ctx->jrdev, ctx->sh_desc_givenc_dma))
+ dma_unmap_single(ctx->jrdev, ctx->sh_desc_givenc_dma,
+ desc_bytes(ctx->sh_desc_givenc),
DMA_TO_DEVICE);
- kfree(ctx->key);
}
-static void __exit caam_algapi_exit(void)
+void caam_algapi_shutdown(struct platform_device *pdev)
{
-
- struct device_node *dev_node;
- struct platform_device *pdev;
struct device *ctrldev;
struct caam_drv_private *priv;
struct caam_crypto_alg *t_alg, *n;
int i, err;
- dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0");
- if (!dev_node)
- return;
-
- pdev = of_find_device_by_node(dev_node);
- if (!pdev)
- return;
-
ctrldev = &pdev->dev;
- of_node_put(dev_node);
priv = dev_get_drvdata(ctrldev);
if (!priv->alg_list.next)
@@ -1175,12 +2203,20 @@ static struct caam_crypto_alg *caam_alg_alloc(struct device *ctrldev,
alg->cra_init = caam_cra_init;
alg->cra_exit = caam_cra_exit;
alg->cra_priority = CAAM_CRA_PRIORITY;
- alg->cra_flags = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC;
alg->cra_blocksize = template->blocksize;
alg->cra_alignmask = 0;
- alg->cra_type = &crypto_aead_type;
alg->cra_ctxsize = sizeof(struct caam_ctx);
- alg->cra_u.aead = template->aead;
+ alg->cra_flags = CRYPTO_ALG_ASYNC | template->type;
+ switch (template->type) {
+ case CRYPTO_ALG_TYPE_ABLKCIPHER:
+ alg->cra_type = &crypto_ablkcipher_type;
+ alg->cra_ablkcipher = template->template_ablkcipher;
+ break;
+ case CRYPTO_ALG_TYPE_AEAD:
+ alg->cra_type = &crypto_aead_type;
+ alg->cra_aead = template->template_aead;
+ break;
+ }
t_alg->class1_alg_type = template->class1_alg_type;
t_alg->class2_alg_type = template->class2_alg_type;
@@ -1190,26 +2226,14 @@ static struct caam_crypto_alg *caam_alg_alloc(struct device *ctrldev,
return t_alg;
}
-static int __init caam_algapi_init(void)
+int caam_algapi_startup(struct platform_device *pdev)
{
- struct device_node *dev_node;
- struct platform_device *pdev;
struct device *ctrldev, **jrdev;
struct caam_drv_private *priv;
int i = 0, err = 0;
- dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0");
- if (!dev_node)
- return -ENODEV;
-
- pdev = of_find_device_by_node(dev_node);
- if (!pdev)
- return -ENODEV;
-
ctrldev = &pdev->dev;
priv = dev_get_drvdata(ctrldev);
- of_node_put(dev_node);
-
INIT_LIST_HEAD(&priv->alg_list);
jrdev = kmalloc(sizeof(*jrdev) * priv->total_jobrs, GFP_KERNEL);
@@ -1260,9 +2284,51 @@ static int __init caam_algapi_init(void)
return err;
}
+#ifdef CONFIG_OF
+static void __exit caam_algapi_exit(void)
+{
+
+ struct device_node *dev_node;
+ struct platform_device *pdev;
+
+ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0");
+ if (!dev_node)
+ return;
+
+ pdev = of_find_device_by_node(dev_node);
+ if (!pdev)
+ return;
+
+ caam_algapi_shutdown(pdev);
+
+ of_node_put(dev_node);
+}
+
+static int __init caam_algapi_init(void)
+{
+ struct device_node *dev_node;
+ struct platform_device *pdev;
+ int stat;
+
+ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0");
+ if (!dev_node)
+ return -ENODEV;
+
+ pdev = of_find_device_by_node(dev_node);
+ if (!pdev)
+ return -ENODEV;
+
+ stat = caam_algapi_startup(pdev);
+
+ of_node_put(dev_node);
+
+ return stat;
+}
+
module_init(caam_algapi_init);
module_exit(caam_algapi_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("FSL CAAM support for crypto API");
MODULE_AUTHOR("Freescale Semiconductor - NMG/STC");
+#endif
diff --git a/drivers/crypto/caam/compat.h b/drivers/crypto/caam/compat.h
index 950450346f70..9ad93d4156c2 100644
--- a/drivers/crypto/caam/compat.h
+++ b/drivers/crypto/caam/compat.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2008-2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008-2012 Freescale Semiconductor, Inc.
*/
#ifndef CAAM_COMPAT_H
@@ -12,7 +12,6 @@
#include <linux/interrupt.h>
#include <linux/crypto.h>
#include <linux/hw_random.h>
-#include <linux/of_platform.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/spinlock.h>
@@ -22,6 +21,18 @@
#include <linux/types.h>
#include <linux/debugfs.h>
#include <linux/circ_buf.h>
+
+#ifdef CONFIG_OF
+#include <linux/of_platform.h>
+#else
+#include <linux/platform_device.h>
+#endif
+
+#ifdef CONFIG_ARM /* needs the clock control subsystem */
+#include <linux/clk.h>
+#include <asm/cacheflush.h>
+#endif
+
#include <net/xfrm.h>
#include <crypto/algapi.h>
@@ -31,5 +42,6 @@
#include <crypto/aead.h>
#include <crypto/authenc.h>
#include <crypto/scatterwalk.h>
+#include <crypto/internal/skcipher.h>
#endif /* !defined(CAAM_COMPAT_H) */
diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c
index 9009713a3c2e..7c1132097c0f 100644
--- a/drivers/crypto/caam/ctrl.c
+++ b/drivers/crypto/caam/ctrl.c
@@ -2,7 +2,7 @@
* CAAM control-plane driver backend
* Controller-level driver, kernel property detection, initialization
*
- * Copyright 2008-2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008-2012 Freescale Semiconductor, Inc.
*/
#include "compat.h"
@@ -22,6 +22,9 @@ static int caam_remove(struct platform_device *pdev)
ctrlpriv = dev_get_drvdata(ctrldev);
topregs = (struct caam_full __iomem *)ctrlpriv->ctrl;
+#ifndef CONFIG_OF
+ caam_algapi_shutdown(pdev);
+#endif
/* shut down JobRs */
for (ring = 0; ring < ctrlpriv->total_jobrs; ring++) {
ret |= caam_jr_shutdown(ctrlpriv->jrdev[ring]);
@@ -37,6 +40,9 @@ static int caam_remove(struct platform_device *pdev)
/* Unmap controller region */
iounmap(&topregs->ctrl);
+ /* shut clocks off before finalizing shutdown */
+ clk_disable(ctrlpriv->caam_clk);
+
kfree(ctrlpriv->jrdev);
kfree(ctrlpriv);
@@ -48,13 +54,24 @@ static int caam_probe(struct platform_device *pdev)
{
int d, ring, rspec;
struct device *dev;
- struct device_node *nprop, *np;
+ struct device_node *np;
struct caam_ctrl __iomem *ctrl;
struct caam_full __iomem *topregs;
struct caam_drv_private *ctrlpriv;
- struct caam_perfmon *perfmon;
struct caam_deco **deco;
u32 deconum;
+#ifdef CONFIG_DEBUG_FS
+ struct caam_perfmon *perfmon;
+#endif
+#ifdef CONFIG_OF
+ struct device_node *nprop;
+#else
+ struct resource *res;
+ char *rname, inst;
+#endif
+#ifdef CONFIG_ARM
+ int ret = 0;
+#endif
ctrlpriv = kzalloc(sizeof(struct caam_drv_private), GFP_KERNEL);
if (!ctrlpriv)
@@ -63,22 +80,71 @@ static int caam_probe(struct platform_device *pdev)
dev = &pdev->dev;
dev_set_drvdata(dev, ctrlpriv);
ctrlpriv->pdev = pdev;
- nprop = pdev->dev.of_node;
/* Get configuration properties from device tree */
/* First, get register page */
+#ifdef CONFIG_OF
+ nprop = pdev->dev.of_node;
ctrl = of_iomap(nprop, 0);
if (ctrl == NULL) {
dev_err(dev, "caam: of_iomap() failed\n");
return -ENOMEM;
}
+#else
+ /* Get the named resource for the controller base address */
+ res = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "iobase_caam");
+ if (!res) {
+ dev_err(dev, "caam: invalid address resource type\n");
+ return -ENODEV;
+ }
+ ctrl = ioremap(res->start, SZ_64K);
+ if (ctrl == NULL) {
+ dev_err(dev, "caam: ioremap() failed\n");
+ return -ENOMEM;
+ }
+#endif
+
ctrlpriv->ctrl = (struct caam_ctrl __force *)ctrl;
/* topregs used to derive pointers to CAAM sub-blocks only */
topregs = (struct caam_full __iomem *)ctrl;
/* Get the IRQ of the controller (for security violations only) */
+#ifdef CONFIG_OF
ctrlpriv->secvio_irq = of_irq_to_resource(nprop, 0, NULL);
+#else
+ res = platform_get_resource_byname(pdev,
+ IORESOURCE_IRQ, "irq_sec_vio");
+ if (!res) {
+ dev_err(dev, "caam: invalid IRQ resource type\n");
+ return -ENODEV;
+ }
+ ctrlpriv->secvio_irq = res->start;
+#endif
+
+/*
+ * ARM targets tend to have clock control subsystems that can
+ * enable/disable clocking to our device. Turn clocking on to proceed
+ */
+#ifdef CONFIG_ARM
+ ctrlpriv->caam_clk = clk_get(&ctrlpriv->pdev->dev, "caam_clk");
+ if (IS_ERR(ctrlpriv->caam_clk)) {
+ ret = PTR_ERR(ctrlpriv->caam_clk);
+ dev_err(&ctrlpriv->pdev->dev,
+ "can't identify CAAM bus clk: %d\n", ret);
+ return -ENODEV;
+ }
+
+ ret = clk_enable(ctrlpriv->caam_clk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "can't enable CAAM bus clock: %d\n", ret);
+ return -ENODEV;
+ }
+
+ pr_debug("%s caam_clk:%d\n", __func__,
+ (int)clk_get_rate(ctrlpriv->caam_clk));
+#endif
/*
* Enable DECO watchdogs and, if this is a PHYS_ADDR_T_64BIT kernel,
@@ -87,8 +153,11 @@ static int caam_probe(struct platform_device *pdev)
setbits32(&topregs->ctrl.mcr, MCFGR_WDENABLE |
(sizeof(dma_addr_t) == sizeof(u64) ? MCFGR_LONG_PTR : 0));
+ /* Set DMA masks according to platform ranging */
if (sizeof(dma_addr_t) == sizeof(u64))
dma_set_mask(dev, DMA_BIT_MASK(36));
+ else
+ dma_set_mask(dev, DMA_BIT_MASK(32));
/* Find out how many DECOs are present */
deconum = (rd_reg64(&topregs->ctrl.perfmon.cha_num) &
@@ -100,15 +169,41 @@ static int caam_probe(struct platform_device *pdev)
deco = (struct caam_deco __force **)&topregs->deco;
for (d = 0; d < deconum; d++)
ctrlpriv->deco[d] = deco[d];
-
/*
* Detect and enable JobRs
* First, find out how many ring spec'ed, allocate references
* for all, then go probe each one.
*/
rspec = 0;
+#ifdef CONFIG_OF
for_each_compatible_node(np, NULL, "fsl,sec-v4.0-job-ring")
rspec++;
+#else
+ np = NULL;
+
+ /* Build the name of the IRQ platform resources to identify */
+ rname = kzalloc(strlen(JR_IRQRES_NAME_ROOT) + 1, 0);
+ if (rname == NULL) {
+ iounmap(&topregs->ctrl);
+ return -ENOMEM;
+ }
+
+ /*
+ * Emulate behavor of for_each_compatible_node() for non OF targets
+ * Identify all IRQ platform resources present
+ */
+ for (d = 0; d < 4; d++) {
+ rname[0] = 0;
+ inst = '0' + d;
+ strcat(rname, JR_IRQRES_NAME_ROOT);
+ strncat(rname, &inst, 1);
+ res = platform_get_resource_byname(pdev,
+ IORESOURCE_IRQ, rname);
+ if (res)
+ rspec++;
+ }
+ kfree(rname);
+#endif
ctrlpriv->jrdev = kzalloc(sizeof(struct device *) * rspec, GFP_KERNEL);
if (ctrlpriv->jrdev == NULL) {
iounmap(&topregs->ctrl);
@@ -117,7 +212,11 @@ static int caam_probe(struct platform_device *pdev)
ring = 0;
ctrlpriv->total_jobrs = 0;
+#ifdef CONFIG_OF
for_each_compatible_node(np, NULL, "fsl,sec-v4.0-job-ring") {
+#else
+ for (d = 0; d < rspec; d++) {
+#endif
caam_jr_probe(pdev, np, ring);
ctrlpriv->total_jobrs++;
ring++;
@@ -164,52 +263,52 @@ static int caam_probe(struct platform_device *pdev)
/* Controller-level - performance monitor counters */
ctrlpriv->ctl_rq_dequeued =
debugfs_create_u64("rq_dequeued",
- S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH,
+ S_IRUSR | S_IRGRP | S_IROTH,
ctrlpriv->ctl, &perfmon->req_dequeued);
ctrlpriv->ctl_ob_enc_req =
debugfs_create_u64("ob_rq_encrypted",
- S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH,
+ S_IRUSR | S_IRGRP | S_IROTH,
ctrlpriv->ctl, &perfmon->ob_enc_req);
ctrlpriv->ctl_ib_dec_req =
debugfs_create_u64("ib_rq_decrypted",
- S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH,
+ S_IRUSR | S_IRGRP | S_IROTH,
ctrlpriv->ctl, &perfmon->ib_dec_req);
ctrlpriv->ctl_ob_enc_bytes =
debugfs_create_u64("ob_bytes_encrypted",
- S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH,
+ S_IRUSR | S_IRGRP | S_IROTH,
ctrlpriv->ctl, &perfmon->ob_enc_bytes);
ctrlpriv->ctl_ob_prot_bytes =
debugfs_create_u64("ob_bytes_protected",
- S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH,
+ S_IRUSR | S_IRGRP | S_IROTH,
ctrlpriv->ctl, &perfmon->ob_prot_bytes);
ctrlpriv->ctl_ib_dec_bytes =
debugfs_create_u64("ib_bytes_decrypted",
- S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH,
+ S_IRUSR | S_IRGRP | S_IROTH,
ctrlpriv->ctl, &perfmon->ib_dec_bytes);
ctrlpriv->ctl_ib_valid_bytes =
debugfs_create_u64("ib_bytes_validated",
- S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH,
+ S_IRUSR | S_IRGRP | S_IROTH,
ctrlpriv->ctl, &perfmon->ib_valid_bytes);
/* Controller level - global status values */
ctrlpriv->ctl_faultaddr =
debugfs_create_u64("fault_addr",
- S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH,
+ S_IRUSR | S_IRGRP | S_IROTH,
ctrlpriv->ctl, &perfmon->faultaddr);
ctrlpriv->ctl_faultdetail =
debugfs_create_u32("fault_detail",
- S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH,
+ S_IRUSR | S_IRGRP | S_IROTH,
ctrlpriv->ctl, &perfmon->faultdetail);
ctrlpriv->ctl_faultstatus =
debugfs_create_u32("fault_status",
- S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH,
+ S_IRUSR | S_IRGRP | S_IROTH,
ctrlpriv->ctl, &perfmon->status);
/* Internal covering keys (useful in non-secure mode only) */
ctrlpriv->ctl_kek_wrap.data = &ctrlpriv->ctrl->kek[0];
ctrlpriv->ctl_kek_wrap.size = KEK_KEY_SIZE * sizeof(u32);
ctrlpriv->ctl_kek = debugfs_create_blob("kek",
- S_IFCHR | S_IRUSR |
+ S_IRUSR |
S_IRGRP | S_IROTH,
ctrlpriv->ctl,
&ctrlpriv->ctl_kek_wrap);
@@ -217,7 +316,7 @@ static int caam_probe(struct platform_device *pdev)
ctrlpriv->ctl_tkek_wrap.data = &ctrlpriv->ctrl->tkek[0];
ctrlpriv->ctl_tkek_wrap.size = KEK_KEY_SIZE * sizeof(u32);
ctrlpriv->ctl_tkek = debugfs_create_blob("tkek",
- S_IFCHR | S_IRUSR |
+ S_IRUSR |
S_IRGRP | S_IROTH,
ctrlpriv->ctl,
&ctrlpriv->ctl_tkek_wrap);
@@ -225,14 +324,27 @@ static int caam_probe(struct platform_device *pdev)
ctrlpriv->ctl_tdsk_wrap.data = &ctrlpriv->ctrl->tdsk[0];
ctrlpriv->ctl_tdsk_wrap.size = KEK_KEY_SIZE * sizeof(u32);
ctrlpriv->ctl_tdsk = debugfs_create_blob("tdsk",
- S_IFCHR | S_IRUSR |
+ S_IRUSR |
S_IRGRP | S_IROTH,
ctrlpriv->ctl,
&ctrlpriv->ctl_tdsk_wrap);
#endif
+
+/*
+ * Non OF configurations use plaform_device, and therefore cannot simply
+ * go and get a device node by name, which the algapi module startup code
+ * assumes is possible. Therefore, non OF configurations will have to
+ * start up the API code explicitly, and forego modularization
+ */
+#ifndef CONFIG_OF
+ /* FIXME: check status */
+ caam_algapi_startup(pdev);
+#endif
+
return 0;
}
+#ifdef CONFIG_OF
static struct of_device_id caam_match[] = {
{
.compatible = "fsl,sec-v4.0",
@@ -240,12 +352,17 @@ static struct of_device_id caam_match[] = {
{},
};
MODULE_DEVICE_TABLE(of, caam_match);
+#endif /* CONFIG_OF */
static struct platform_driver caam_driver = {
.driver = {
.name = "caam",
.owner = THIS_MODULE,
+#ifdef CONFIG_OF
.of_match_table = caam_match,
+#else
+
+#endif
},
.probe = caam_probe,
.remove = __devexit_p(caam_remove),
@@ -253,12 +370,20 @@ static struct platform_driver caam_driver = {
static int __init caam_base_init(void)
{
+#ifdef CONFIG_OF
+ return of_register_platform_driver(&caam_driver);
+#else
return platform_driver_register(&caam_driver);
+#endif
}
static void __exit caam_base_exit(void)
{
+#ifdef CONFIG_OF
+ return of_unregister_platform_driver(&caam_driver);
+#else
return platform_driver_unregister(&caam_driver);
+#endif
}
module_init(caam_base_init);
diff --git a/drivers/crypto/caam/desc_constr.h b/drivers/crypto/caam/desc_constr.h
index 46915800c26f..1e5d54c72aa8 100644
--- a/drivers/crypto/caam/desc_constr.h
+++ b/drivers/crypto/caam/desc_constr.h
@@ -1,7 +1,7 @@
/*
* caam descriptor construction helper functions
*
- * Copyright 2008-2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008-2012 Freescale Semiconductor, Inc.
*/
#include "desc.h"
@@ -9,7 +9,7 @@
#define IMMEDIATE (1 << 23)
#define CAAM_CMD_SZ sizeof(u32)
#define CAAM_PTR_SZ sizeof(dma_addr_t)
-#define CAAM_DESC_BYTES_MAX (CAAM_CMD_SZ * 64)
+#define CAAM_DESC_BYTES_MAX (CAAM_CMD_SZ * MAX_CAAM_DESCSIZE)
#ifdef DEBUG
#define PRINT_POS do { printk(KERN_DEBUG "%02d: %s\n", desc_len(desc),\
@@ -18,6 +18,9 @@
#define PRINT_POS
#endif
+#define SET_OK_PROP_ERRORS (IMMEDIATE | LDST_CLASS_DECO | \
+ LDST_SRCDST_WORD_DECOCTRL | \
+ (LDOFF_CHG_SHARE_OK_PROP << LDST_OFFSET_SHIFT))
#define DISABLE_AUTO_INFO_FIFO (IMMEDIATE | LDST_CLASS_DECO | \
LDST_SRCDST_WORD_DECOCTRL | \
(LDOFF_DISABLE_AUTO_NFIFO << LDST_OFFSET_SHIFT))
@@ -203,3 +206,56 @@ static inline void append_##cmd##_imm_##type(u32 *desc, type immediate, \
append_cmd(desc, immediate); \
}
APPEND_CMD_RAW_IMM(load, LOAD, u32);
+
+/*
+ * Append math command. Only the last part of destination and source need to
+ * be specified
+ */
+#define APPEND_MATH(op, desc, dest, src_0, src_1, len) \
+append_cmd(desc, CMD_MATH | MATH_FUN_##op | MATH_DEST_##dest | \
+ MATH_SRC0_##src_0 | MATH_SRC1_##src_1 | (u32) (len & MATH_LEN_MASK));
+
+#define append_math_add(desc, dest, src0, src1, len) \
+ APPEND_MATH(ADD, desc, dest, src0, src1, len)
+#define append_math_sub(desc, dest, src0, src1, len) \
+ APPEND_MATH(SUB, desc, dest, src0, src1, len)
+#define append_math_add_c(desc, dest, src0, src1, len) \
+ APPEND_MATH(ADDC, desc, dest, src0, src1, len)
+#define append_math_sub_b(desc, dest, src0, src1, len) \
+ APPEND_MATH(SUBB, desc, dest, src0, src1, len)
+#define append_math_and(desc, dest, src0, src1, len) \
+ APPEND_MATH(AND, desc, dest, src0, src1, len)
+#define append_math_or(desc, dest, src0, src1, len) \
+ APPEND_MATH(OR, desc, dest, src0, src1, len)
+#define append_math_xor(desc, dest, src0, src1, len) \
+ APPEND_MATH(XOR, desc, dest, src0, src1, len)
+#define append_math_lshift(desc, dest, src0, src1, len) \
+ APPEND_MATH(LSHIFT, desc, dest, src0, src1, len)
+#define append_math_rshift(desc, dest, src0, src1, len) \
+ APPEND_MATH(RSHIFT, desc, dest, src0, src1, len)
+
+/* Exactly one source is IMM. Data is passed in as u32 value */
+#define APPEND_MATH_IMM_u32(op, desc, dest, src_0, src_1, data) \
+do { \
+ APPEND_MATH(op, desc, dest, src_0, src_1, CAAM_CMD_SZ); \
+ append_cmd(desc, data); \
+} while (0);
+
+#define append_math_add_imm_u32(desc, dest, src0, src1, data) \
+ APPEND_MATH_IMM_u32(ADD, desc, dest, src0, src1, data)
+#define append_math_sub_imm_u32(desc, dest, src0, src1, data) \
+ APPEND_MATH_IMM_u32(SUB, desc, dest, src0, src1, data)
+#define append_math_add_c_imm_u32(desc, dest, src0, src1, data) \
+ APPEND_MATH_IMM_u32(ADDC, desc, dest, src0, src1, data)
+#define append_math_sub_b_imm_u32(desc, dest, src0, src1, data) \
+ APPEND_MATH_IMM_u32(SUBB, desc, dest, src0, src1, data)
+#define append_math_and_imm_u32(desc, dest, src0, src1, data) \
+ APPEND_MATH_IMM_u32(AND, desc, dest, src0, src1, data)
+#define append_math_or_imm_u32(desc, dest, src0, src1, data) \
+ APPEND_MATH_IMM_u32(OR, desc, dest, src0, src1, data)
+#define append_math_xor_imm_u32(desc, dest, src0, src1, data) \
+ APPEND_MATH_IMM_u32(XOR, desc, dest, src0, src1, data)
+#define append_math_lshift_imm_u32(desc, dest, src0, src1, data) \
+ APPEND_MATH_IMM_u32(LSHIFT, desc, dest, src0, src1, data)
+#define append_math_rshift_imm_u32(desc, dest, src0, src1, data) \
+ APPEND_MATH_IMM_u32(RSHIFT, desc, dest, src0, src1, data)
diff --git a/drivers/crypto/caam/intern.h b/drivers/crypto/caam/intern.h
index a34be01b0b29..19e4f25c25a8 100644
--- a/drivers/crypto/caam/intern.h
+++ b/drivers/crypto/caam/intern.h
@@ -2,7 +2,7 @@
* CAAM/SEC 4.x driver backend
* Private/internal definitions between modules
*
- * Copyright 2008-2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008-2012 Freescale Semiconductor, Inc.
*
*/
@@ -26,6 +26,23 @@
#define JOBR_INTC_COUNT_THLD 0
#endif
+#ifndef CONFIG_OF
+#define JR_IRQRES_NAME_ROOT "irq_jr"
+#define JR_MEMRES_NAME_ROOT "offset_jr"
+#endif
+
+#ifdef CONFIG_ARM
+/*
+ * FIXME: ARM tree doesn't seem to provide this, ergo it seems to be
+ * in "platform limbo". Find a better place, perhaps.
+ */
+static inline void irq_dispose_mapping(unsigned int virq)
+{
+ return;
+}
+#endif
+
+
/*
* Storage for tracking each in-process entry moving across a ring
* Each entry on an output ring needs one of these
@@ -91,6 +108,10 @@ struct caam_drv_private {
/* list of registered crypto algorithms (mk generic context handle?) */
struct list_head alg_list;
+#ifdef CONFIG_ARM
+ struct clk *caam_clk;
+#endif
+
/*
* debugfs entries for developer view into driver/device
* variables at runtime.
@@ -108,6 +129,7 @@ struct caam_drv_private {
#endif
};
-void caam_jr_algapi_init(struct device *dev);
-void caam_jr_algapi_remove(struct device *dev);
+void caam_algapi_shutdown(struct platform_device *pdev);
+int caam_algapi_startup(struct platform_device *pdev);
+
#endif /* INTERN_H */
diff --git a/drivers/crypto/caam/jr.c b/drivers/crypto/caam/jr.c
index 340fa322c0f0..280aaaa3d4d4 100644
--- a/drivers/crypto/caam/jr.c
+++ b/drivers/crypto/caam/jr.c
@@ -2,7 +2,7 @@
* CAAM/SEC 4.x transport/backend driver
* JobR backend functionality
*
- * Copyright 2008-2011 Freescale Semiconductor, Inc.
+ * Copyright 2008-2012 Freescale Semiconductor, Inc.
*/
#include "compat.h"
@@ -57,9 +57,15 @@ static void caam_jr_dequeue(unsigned long devarg)
struct caam_drv_private_jr *jrp = dev_get_drvdata(dev);
void (*usercall)(struct device *dev, u32 *desc, u32 status, void *arg);
u32 *userdesc, userstatus;
+ dma_addr_t outbusaddr;
void *userarg;
unsigned long flags;
+ outbusaddr = rd_reg64(&jrp->rregs->outring_base);
+ dma_sync_single_for_cpu(dev, outbusaddr,
+ sizeof(struct jr_outentry) * JOBR_DEPTH,
+ DMA_FROM_DEVICE);
+
spin_lock_irqsave(&jrp->outlock, flags);
head = ACCESS_ONCE(jrp->head);
@@ -73,7 +79,6 @@ static void caam_jr_dequeue(unsigned long devarg)
sw_idx = (tail + i) & (JOBR_DEPTH - 1);
smp_read_barrier_depends();
-
if (jrp->outring[hw_idx].desc ==
jrp->entinfo[sw_idx].desc_addr_dma)
break; /* found */
@@ -240,7 +245,7 @@ int caam_jr_enqueue(struct device *dev, u32 *desc,
struct caam_jrentry_info *head_entry;
unsigned long flags;
int head, tail, desc_size;
- dma_addr_t desc_dma;
+ dma_addr_t desc_dma, inpbusaddr;
desc_size = (*desc & HDR_JD_LENGTH_MASK) * sizeof(u32);
desc_dma = dma_map_single(dev, desc, desc_size, DMA_TO_DEVICE);
@@ -249,6 +254,12 @@ int caam_jr_enqueue(struct device *dev, u32 *desc,
return -EIO;
}
+ dma_sync_single_for_device(dev, desc_dma, desc_size, DMA_TO_DEVICE);
+
+ inpbusaddr = rd_reg64(&jrp->rregs->inpring_base);
+ dma_sync_single_for_device(dev, inpbusaddr,
+ sizeof(dma_addr_t) * JOBR_DEPTH,
+ DMA_TO_DEVICE);
spin_lock_irqsave(&jrp->inplock, flags);
head = jrp->head;
@@ -270,6 +281,10 @@ int caam_jr_enqueue(struct device *dev, u32 *desc,
jrp->inpring[jrp->inp_ring_write_index] = desc_dma;
+ dma_sync_single_for_device(dev, inpbusaddr,
+ sizeof(dma_addr_t) * JOBR_DEPTH,
+ DMA_TO_DEVICE);
+
smp_wmb();
jrp->inp_ring_write_index = (jrp->inp_ring_write_index + 1) &
@@ -343,7 +358,7 @@ static int caam_jr_init(struct device *dev)
(unsigned long)dev);
error = request_irq(jrp->irq, caam_jr_interrupt, IRQF_SHARED,
- "caam-jobr", dev);
+ "caam-jr", dev);
if (error) {
dev_err(dev, "can't connect JobR %d interrupt (%d)\n",
jrp->ridx, jrp->irq);
@@ -377,7 +392,7 @@ static int caam_jr_init(struct device *dev)
/* Setup rings */
inpbusaddr = dma_map_single(dev, jrp->inpring,
sizeof(u32 *) * JOBR_DEPTH,
- DMA_BIDIRECTIONAL);
+ DMA_TO_DEVICE);
if (dma_mapping_error(dev, inpbusaddr)) {
dev_err(dev, "caam_jr_init(): can't map input ring\n");
kfree(jrp->inpring);
@@ -388,12 +403,12 @@ static int caam_jr_init(struct device *dev)
outbusaddr = dma_map_single(dev, jrp->outring,
sizeof(struct jr_outentry) * JOBR_DEPTH,
- DMA_BIDIRECTIONAL);
+ DMA_FROM_DEVICE);
if (dma_mapping_error(dev, outbusaddr)) {
dev_err(dev, "caam_jr_init(): can't map output ring\n");
dma_unmap_single(dev, inpbusaddr,
sizeof(u32 *) * JOBR_DEPTH,
- DMA_BIDIRECTIONAL);
+ DMA_TO_DEVICE);
kfree(jrp->inpring);
kfree(jrp->outring);
kfree(jrp->entinfo);
@@ -446,11 +461,9 @@ int caam_jr_shutdown(struct device *dev)
outbusaddr = rd_reg64(&jrp->rregs->outring_base);
dma_unmap_single(dev, outbusaddr,
sizeof(struct jr_outentry) * JOBR_DEPTH,
- DMA_BIDIRECTIONAL);
+ DMA_FROM_DEVICE);
dma_unmap_single(dev, inpbusaddr, sizeof(u32 *) * JOBR_DEPTH,
- DMA_BIDIRECTIONAL);
- kfree(jrp->outring);
- kfree(jrp->inpring);
+ DMA_TO_DEVICE);
kfree(jrp->entinfo);
return ret;
@@ -467,8 +480,12 @@ int caam_jr_probe(struct platform_device *pdev, struct device_node *np,
struct platform_device *jr_pdev;
struct caam_drv_private *ctrlpriv;
struct caam_drv_private_jr *jrpriv;
- u32 *jroffset;
int error;
+ /* FIXME: perhaps "struct resource *" for OF and non? */
+ u32 *jroffset, *irqres;
+#ifndef CONFIG_OF
+ char *rname, rinst;
+#endif
ctrldev = &pdev->dev;
ctrlpriv = dev_get_drvdata(ctrldev);
@@ -489,12 +506,35 @@ int caam_jr_probe(struct platform_device *pdev, struct device_node *np,
* need to add in the offset to this JobR. Don't know if I
* like this long-term, but it'll run
*/
+#ifdef CONFIG_OF
jroffset = (u32 *)of_get_property(np, "reg", NULL);
+#else
+ rname = kmalloc(strlen(JR_MEMRES_NAME_ROOT) + 1, 0);
+ if (rname == NULL) {
+ dev_err(ctrldev, "can't alloc resource detection buffer %d\n",
+ ring);
+ kfree(jrpriv);
+ return -ENOMEM;
+ }
+ rname[0] = 0;
+ rinst = '0' + ring;
+ strcat(rname, JR_MEMRES_NAME_ROOT);
+ strncat(rname, &rinst, 1);
+ jroffset = (u32 *)platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ rname);
+ kfree(rname);
+#endif
jrpriv->rregs = (struct caam_job_ring __iomem *)((void *)ctrlpriv->ctrl
+ *jroffset);
/* Build a local dev for each detected queue */
+#ifdef CONFIG_OF
jr_pdev = of_platform_device_create(np, NULL, ctrldev);
+#else
+ jr_pdev = platform_device_register_data(ctrldev, "caam_jr", ring,
+ jrpriv,
+ sizeof(struct caam_drv_private_jr));
+#endif
if (jr_pdev == NULL) {
kfree(jrpriv);
return -EINVAL;
@@ -504,7 +544,24 @@ int caam_jr_probe(struct platform_device *pdev, struct device_node *np,
ctrlpriv->jrdev[ring] = jrdev;
/* Identify the interrupt */
+#ifdef CONFIG_OF
jrpriv->irq = of_irq_to_resource(np, 0, NULL);
+#else
+ rname = kmalloc(strlen(JR_IRQRES_NAME_ROOT) + 1, 0);
+ if (rname == NULL) {
+ dev_err(ctrldev, "can't alloc resource detection buffer %d\n",
+ ring);
+ kfree(jrpriv);
+ return -ENOMEM;
+ }
+ rname[0] = 0;
+ strcat(rname, JR_IRQRES_NAME_ROOT);
+ strncat(rname, &rinst, 1);
+ irqres = (u32 *)platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+ rname);
+ jrpriv->irq = *irqres;
+ kfree(rname);
+#endif
/* Now do the platform independent part */
error = caam_jr_init(jrdev); /* now turn on hardware */
diff --git a/drivers/crypto/caam/regs.h b/drivers/crypto/caam/regs.h
index aee394e39056..888706d2801c 100644
--- a/drivers/crypto/caam/regs.h
+++ b/drivers/crypto/caam/regs.h
@@ -1,7 +1,7 @@
/*
* CAAM hardware register-level view
*
- * Copyright 2008-2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008-2012 Freescale Semiconductor, Inc.
*/
#ifndef REGS_H
@@ -74,15 +74,21 @@
#endif
#else
#ifdef __LITTLE_ENDIAN
-#define wr_reg32(reg, data) __raw_writel(reg, data)
-#define rd_reg32(reg) __raw_readl(reg)
+#define wr_reg32(reg, data) writel(data, reg)
+#define rd_reg32(reg) readl(reg)
#ifdef CONFIG_64BIT
-#define wr_reg64(reg, data) __raw_writeq(reg, data)
-#define rd_reg64(reg) __raw_readq(reg)
+#define wr_reg64(reg, data) writeq(data, reg)
+#define rd_reg64(reg) readq(reg)
#endif
#endif
#endif
+#ifdef CONFIG_ARM
+/* These are common macros for Power, put here for ARMs */
+#define setbits32(_addr, _v) writel((readl(_addr) | (_v)), (_addr))
+#define clrbits32(_addr, _v) writel((readl(_addr) & ~(_v)), (_addr))
+#endif
+
#ifndef CONFIG_64BIT
static inline void wr_reg64(u64 __iomem *reg, u64 data)
{
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index 306dcdd0dc38..a4ed05342168 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -340,9 +340,9 @@ static int sdma_config_ownership(struct sdma_channel *sdmac,
if (event_override && mcu_override && dsp_override)
return -EINVAL;
- evt = __raw_readl(sdma->regs + SDMA_H_EVTOVR);
- mcu = __raw_readl(sdma->regs + SDMA_H_HOSTOVR);
- dsp = __raw_readl(sdma->regs + SDMA_H_DSPOVR);
+ evt = readl_relaxed(sdma->regs + SDMA_H_EVTOVR);
+ mcu = readl_relaxed(sdma->regs + SDMA_H_HOSTOVR);
+ dsp = readl_relaxed(sdma->regs + SDMA_H_DSPOVR);
if (dsp_override)
dsp &= ~(1 << channel);
@@ -359,9 +359,9 @@ static int sdma_config_ownership(struct sdma_channel *sdmac,
else
mcu |= (1 << channel);
- __raw_writel(evt, sdma->regs + SDMA_H_EVTOVR);
- __raw_writel(mcu, sdma->regs + SDMA_H_HOSTOVR);
- __raw_writel(dsp, sdma->regs + SDMA_H_DSPOVR);
+ writel_relaxed(evt, sdma->regs + SDMA_H_EVTOVR);
+ writel_relaxed(mcu, sdma->regs + SDMA_H_HOSTOVR);
+ writel_relaxed(dsp, sdma->regs + SDMA_H_DSPOVR);
return 0;
}
@@ -378,7 +378,7 @@ static int sdma_run_channel(struct sdma_channel *sdmac)
init_completion(&sdmac->done);
wmb();
- __raw_writel(1 << channel, sdma->regs + SDMA_H_START);
+ writel_relaxed(1 << channel, sdma->regs + SDMA_H_START);
ret = wait_for_completion_timeout(&sdmac->done, HZ);
@@ -421,9 +421,9 @@ static void sdma_event_enable(struct sdma_channel *sdmac, unsigned int event)
u32 val;
u32 chnenbl = chnenbl_ofs(sdma, event);
- val = __raw_readl(sdma->regs + chnenbl);
+ val = readl_relaxed(sdma->regs + chnenbl);
val |= (1 << channel);
- __raw_writel(val, sdma->regs + chnenbl);
+ writel_relaxed(val, sdma->regs + chnenbl);
}
static void sdma_event_disable(struct sdma_channel *sdmac, unsigned int event)
@@ -433,9 +433,9 @@ static void sdma_event_disable(struct sdma_channel *sdmac, unsigned int event)
u32 chnenbl = chnenbl_ofs(sdma, event);
u32 val;
- val = __raw_readl(sdma->regs + chnenbl);
+ val = readl_relaxed(sdma->regs + chnenbl);
val &= ~(1 << channel);
- __raw_writel(val, sdma->regs + chnenbl);
+ writel_relaxed(val, sdma->regs + chnenbl);
}
static void sdma_handle_channel_loop(struct sdma_channel *sdmac)
@@ -512,8 +512,8 @@ static irqreturn_t sdma_int_handler(int irq, void *dev_id)
struct sdma_engine *sdma = dev_id;
u32 stat;
- stat = __raw_readl(sdma->regs + SDMA_H_INTR);
- __raw_writel(stat, sdma->regs + SDMA_H_INTR);
+ stat = readl_relaxed(sdma->regs + SDMA_H_INTR);
+ writel_relaxed(stat, sdma->regs + SDMA_H_INTR);
while (stat) {
int channel = fls(stat) - 1;
@@ -673,7 +673,7 @@ static void sdma_disable_channel(struct sdma_channel *sdmac)
struct sdma_engine *sdma = sdmac->sdma;
int channel = sdmac->channel;
- __raw_writel(1 << channel, sdma->regs + SDMA_H_STATSTOP);
+ writel_relaxed(1 << channel, sdma->regs + SDMA_H_STATSTOP);
sdmac->status = DMA_ERROR;
}
@@ -778,7 +778,7 @@ static int sdma_set_channel_priority(struct sdma_channel *sdmac,
return -EINVAL;
}
- __raw_writel(priority, sdma->regs + SDMA_CHNPRI_0 + 4 * channel);
+ writel_relaxed(priority, sdma->regs + SDMA_CHNPRI_0 + 4 * channel);
return 0;
}
@@ -817,7 +817,7 @@ out:
static void sdma_enable_channel(struct sdma_engine *sdma, int channel)
{
wmb();
- __raw_writel(1 << channel, sdma->regs + SDMA_H_START);
+ writel(1 << channel, sdma->regs + SDMA_H_START);
}
static dma_cookie_t sdma_assign_cookie(struct sdma_channel *sdmac)
@@ -1246,7 +1246,7 @@ static int __init sdma_init(struct sdma_engine *sdma)
clk_enable(sdma->clk);
/* Be sure SDMA has not started yet */
- __raw_writel(0, sdma->regs + SDMA_H_C0PTR);
+ writel_relaxed(0, sdma->regs + SDMA_H_C0PTR);
sdma->channel_control = dma_alloc_coherent(NULL,
MAX_DMA_CHANNELS * sizeof (struct sdma_channel_control) +
@@ -1269,11 +1269,11 @@ static int __init sdma_init(struct sdma_engine *sdma)
/* disable all channels */
for (i = 0; i < sdma->num_events; i++)
- __raw_writel(0, sdma->regs + chnenbl_ofs(sdma, i));
+ writel_relaxed(0, sdma->regs + chnenbl_ofs(sdma, i));
/* All channels have priority 0 */
for (i = 0; i < MAX_DMA_CHANNELS; i++)
- __raw_writel(0, sdma->regs + SDMA_CHNPRI_0 + i * 4);
+ writel_relaxed(0, sdma->regs + SDMA_CHNPRI_0 + i * 4);
ret = sdma_request_channel(&sdma->channel[0]);
if (ret)
@@ -1282,16 +1282,16 @@ static int __init sdma_init(struct sdma_engine *sdma)
sdma_config_ownership(&sdma->channel[0], false, true, false);
/* Set Command Channel (Channel Zero) */
- __raw_writel(0x4050, sdma->regs + SDMA_CHN0ADDR);
+ writel_relaxed(0x4050, sdma->regs + SDMA_CHN0ADDR);
/* Set bits of CONFIG register but with static context switching */
/* FIXME: Check whether to set ACR bit depending on clock ratios */
- __raw_writel(0, sdma->regs + SDMA_H_CONFIG);
+ writel_relaxed(0, sdma->regs + SDMA_H_CONFIG);
- __raw_writel(ccb_phys, sdma->regs + SDMA_H_C0PTR);
+ writel_relaxed(ccb_phys, sdma->regs + SDMA_H_C0PTR);
/* Set bits of CONFIG register with given context switching mode */
- __raw_writel(SDMA_H_CONFIG_CSM, sdma->regs + SDMA_H_CONFIG);
+ writel_relaxed(SDMA_H_CONFIG_CSM, sdma->regs + SDMA_H_CONFIG);
/* Initializes channel's priorities */
sdma_set_channel_priority(&sdma->channel[0], 7);
diff --git a/drivers/dma/pxp/pxp_dma.c b/drivers/dma/pxp/pxp_dma.c
index eb5b4fb60306..ddd1c0cd192d 100644
--- a/drivers/dma/pxp/pxp_dma.c
+++ b/drivers/dma/pxp/pxp_dma.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2010-2012 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -180,6 +180,7 @@ static bool is_yuv(u32 pix_fmt)
(pix_fmt == PXP_PIX_FMT_YUV444) |
(pix_fmt == PXP_PIX_FMT_NV12) |
(pix_fmt == PXP_PIX_FMT_GREY) |
+ (pix_fmt == PXP_PIX_FMT_GY04) |
(pix_fmt == PXP_PIX_FMT_YVU410P) |
(pix_fmt == PXP_PIX_FMT_YUV410P) |
(pix_fmt == PXP_PIX_FMT_YVU420P) |
@@ -215,6 +216,9 @@ static void pxp_set_ctrl(struct pxps *pxp)
case PXP_PIX_FMT_GREY:
fmt_ctrl = BV_PXP_CTRL_S0_FORMAT__YUV420;
break;
+ case PXP_PIX_FMT_GY04:
+ fmt_ctrl = BV_PXP_CTRL_S0_FORMAT__YUV420;
+ break;
case PXP_PIX_FMT_YUV422P:
fmt_ctrl = BV_PXP_CTRL_S0_FORMAT__YUV422;
break;
@@ -243,6 +247,9 @@ static void pxp_set_ctrl(struct pxps *pxp)
case PXP_PIX_FMT_GREY:
fmt_ctrl = BV_PXP_CTRL_OUTBUF_FORMAT__MONOC8;
break;
+ case PXP_PIX_FMT_GY04:
+ fmt_ctrl = BV_PXP_CTRL_OUTBUF_FORMAT__MONOC4;
+ break;
default:
fmt_ctrl = 0;
}
@@ -620,6 +627,14 @@ static void pxp_set_s0buf(struct pxps *pxp)
__raw_writel(U, pxp->base + HW_PXP_S0UBUF);
__raw_writel(V, pxp->base + HW_PXP_S0VBUF);
}
+
+ /* TODO: only support RGB565, Y8 , Y4 */
+ if (s0_params->pixel_fmt == PXP_PIX_FMT_GREY)
+ __raw_writel(s0_params->width, pxp->base + HW_PXP_PS_PITCH);
+ else if (s0_params->pixel_fmt == PXP_PIX_FMT_GY04)
+ __raw_writel(s0_params->width >> 1, pxp->base + HW_PXP_PS_PITCH);
+ else
+ __raw_writel(s0_params->width * 2, pxp->base + HW_PXP_PS_PITCH);
}
/**
diff --git a/drivers/dma/pxp/pxp_dma_v2.c b/drivers/dma/pxp/pxp_dma_v2.c
index 3fadd9a5f762..ceb72edbd39e 100644
--- a/drivers/dma/pxp/pxp_dma_v2.c
+++ b/drivers/dma/pxp/pxp_dma_v2.c
@@ -220,6 +220,7 @@ static bool is_yuv(u32 pix_fmt)
(pix_fmt == PXP_PIX_FMT_YUV444) |
(pix_fmt == PXP_PIX_FMT_NV12) |
(pix_fmt == PXP_PIX_FMT_GREY) |
+ (pix_fmt == PXP_PIX_FMT_GY04) |
(pix_fmt == PXP_PIX_FMT_YVU410P) |
(pix_fmt == PXP_PIX_FMT_YUV410P) |
(pix_fmt == PXP_PIX_FMT_YVU420P) |
@@ -257,6 +258,9 @@ static void pxp_set_ctrl(struct pxps *pxp)
case PXP_PIX_FMT_GREY:
fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__Y8;
break;
+ case PXP_PIX_FMT_GY04:
+ fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__Y4;
+ break;
case PXP_PIX_FMT_YUV422P:
fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__YUV422;
break;
@@ -287,6 +291,9 @@ static void pxp_set_ctrl(struct pxps *pxp)
case PXP_PIX_FMT_GREY:
fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__Y8;
break;
+ case PXP_PIX_FMT_GY04:
+ fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__Y4;
+ break;
default:
fmt_ctrl = 0;
}
@@ -697,9 +704,11 @@ static void pxp_set_s0buf(struct pxps *pxp)
__raw_writel(V, pxp->base + HW_PXP_PS_VBUF);
}
- /* TODO: only support RGB565, Y8 */
+ /* TODO: only support RGB565, Y8, Y4 */
if (s0_params->pixel_fmt == PXP_PIX_FMT_GREY)
__raw_writel(s0_params->width, pxp->base + HW_PXP_PS_PITCH);
+ else if (s0_params->pixel_fmt == PXP_PIX_FMT_GY04)
+ __raw_writel(s0_params->width >> 1, pxp->base + HW_PXP_PS_PITCH);
else
__raw_writel(s0_params->width * 2, pxp->base + HW_PXP_PS_PITCH);
}
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index bf8d1797589c..bf002d471b1f 100755
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -187,6 +187,18 @@ config TOUCHSCREEN_EGALAX
To compile this driver as a module, choose M here: the
module will be called egalax_ts.
+config TOUCHSCREEN_ELAN
+ tristate "ELAN touchscreen input driver"
+ depends on I2C
+ help
+ Say Y here if you have an I2C ELAN touchscreen
+ attached.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called elan-touch.
+
config TOUCHSCREEN_FUJITSU
tristate "Fujitsu serial touchscreen"
select SERIO
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index f61dfe88bc62..28ac2d2c81bd 100755
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -66,3 +66,4 @@ obj-$(CONFIG_TOUCHSCREEN_P1003) += p1003_ts.o
obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o
obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o
obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o
+obj-$(CONFIG_TOUCHSCREEN_ELAN) += elan_ts.o
diff --git a/drivers/input/touchscreen/elan_ts.c b/drivers/input/touchscreen/elan_ts.c
new file mode 100644
index 000000000000..7edd87d6cd1b
--- /dev/null
+++ b/drivers/input/touchscreen/elan_ts.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2007-2008 HTC Corporation.
+ *
+ * This driver is adapted from elan8232_i2c.c written by Shan-Fu Chiou
+ * <sfchiou@gmail.com> and Jay Tu <jay_tu@htc.com>.
+ * This driver is also adapted from the ELAN Touch Screen driver
+ * written by Stanley Zeng <stanley.zeng@emc.com.tw>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/hrtimer.h>
+#include <linux/gpio.h>
+
+static const char ELAN_TS_NAME[] = "elan-touch";
+
+#define ELAN_TS_X_MAX 1088
+#define ELAN_TS_Y_MAX 768
+#define ELAN_USER_X_MAX 800
+#define ELAN_USER_Y_MAX 600
+#define IDX_PACKET_SIZE 8
+
+enum {
+ hello_packet = 0x55,
+ idx_coordinate_packet = 0x5a,
+};
+
+enum {
+ idx_finger_state = 7,
+};
+
+static struct workqueue_struct *elan_wq;
+
+static struct elan_data {
+ int intr_gpio;
+ int use_irq;
+ struct hrtimer timer;
+ struct work_struct work;
+ struct i2c_client *client;
+ struct input_dev *input;
+ wait_queue_head_t wait;
+} elan_touch_data;
+
+/*--------------------------------------------------------------*/
+static int elan_touch_detect_int_level(void)
+{
+ unsigned v;
+ v = gpio_get_value(elan_touch_data.intr_gpio);
+
+ return v;
+}
+
+static int __elan_touch_poll(struct i2c_client *client)
+{
+ int status = 0, retry = 20;
+
+ do {
+ status = elan_touch_detect_int_level();
+ retry--;
+ mdelay(20);
+ } while (status == 1 && retry > 0);
+
+ return (status == 0 ? 0 : -ETIMEDOUT);
+}
+
+static int elan_touch_poll(struct i2c_client *client)
+{
+ return __elan_touch_poll(client);
+}
+
+static int __hello_packet_handler(struct i2c_client *client)
+{
+ int rc;
+ uint8_t buf_recv[4] = { 0 };
+
+ rc = elan_touch_poll(client);
+
+ if (rc < 0) {
+ return -EINVAL;
+ }
+
+ rc = i2c_master_recv(client, buf_recv, 4);
+
+ if (rc != 4) {
+ return rc;
+ } else {
+ int i;
+ printk("hello packet: [0x%02x 0x%02x 0x%02x 0x%02x]\n",
+ buf_recv[0], buf_recv[1], buf_recv[2], buf_recv[3]);
+
+ for (i = 0; i < 4; i++)
+ if (buf_recv[i] != hello_packet)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static inline int elan_touch_parse_xy(uint8_t *data, uint16_t *x,
+ uint16_t *y)
+{
+ *x = (data[0] & 0xf0);
+ *x <<= 4;
+ *x |= data[1];
+ if (*x >= ELAN_TS_X_MAX)
+ *x = ELAN_TS_X_MAX;
+ *x = ((((ELAN_TS_X_MAX -
+ *x) * 1000) / ELAN_TS_X_MAX) * ELAN_USER_X_MAX) / 1000;
+
+ *y = (data[0] & 0x0f);
+ *y <<= 8;
+ *y |= data[2];
+ if (*y >= ELAN_TS_Y_MAX)
+ *y = ELAN_TS_Y_MAX;
+ *y = ((((ELAN_TS_Y_MAX -
+ *y) * 1000) / ELAN_TS_Y_MAX) * ELAN_USER_Y_MAX) / 1000;
+
+ return 0;
+}
+
+/* __elan_touch_init -- hand shaking with touch panel
+ *
+ * 1.recv hello packet
+ */
+static int __elan_touch_init(struct i2c_client *client)
+{
+ int rc;
+ rc = __hello_packet_handler(client);
+ if (rc < 0)
+ goto hand_shake_failed;
+
+hand_shake_failed:
+ return rc;
+}
+
+static int elan_touch_recv_data(struct i2c_client *client, uint8_t * buf)
+{
+ int rc, bytes_to_recv = IDX_PACKET_SIZE;
+
+ if (buf == NULL)
+ return -EINVAL;
+
+ memset(buf, 0, bytes_to_recv);
+ rc = i2c_master_recv(client, buf, bytes_to_recv);
+ if (rc != bytes_to_recv) {
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static void elan_touch_report_data(struct i2c_client *client, uint8_t * buf)
+{
+ switch (buf[0]) {
+ case idx_coordinate_packet:
+ {
+ uint16_t x1, x2, y1, y2;
+ uint8_t finger_stat;
+
+ finger_stat = (buf[idx_finger_state] & 0x06) >> 1;
+
+ if (finger_stat == 0) {
+ input_report_key(elan_touch_data.input, BTN_TOUCH, 0);
+ input_report_key(elan_touch_data.input, BTN_2, 0);
+ } else if (finger_stat == 1) {
+ elan_touch_parse_xy(&buf[1], &x1, &y1);
+ input_report_abs(elan_touch_data.input, ABS_X, x1);
+ input_report_abs(elan_touch_data.input, ABS_Y, y1);
+ input_report_key(elan_touch_data.input, BTN_TOUCH, 1);
+ input_report_key(elan_touch_data.input, BTN_2, 0);
+ } else if (finger_stat == 2) {
+ elan_touch_parse_xy(&buf[1], &x1, &y1);
+ input_report_abs(elan_touch_data.input, ABS_X, x1);
+ input_report_abs(elan_touch_data.input, ABS_Y, y1);
+ input_report_key(elan_touch_data.input, BTN_TOUCH, 1);
+ elan_touch_parse_xy(&buf[4], &x2, &y2);
+ input_report_abs(elan_touch_data.input, ABS_HAT0X, x2);
+ input_report_abs(elan_touch_data.input, ABS_HAT0Y, y2);
+ input_report_key(elan_touch_data.input, BTN_2, 1);
+ }
+ input_sync(elan_touch_data.input);
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+static void elan_touch_work_func(struct work_struct *work)
+{
+ int rc;
+ uint8_t buf[IDX_PACKET_SIZE] = { 0 };
+ struct i2c_client *client = elan_touch_data.client;
+
+ if (elan_touch_detect_int_level())
+ return;
+
+ rc = elan_touch_recv_data(client, buf);
+ if (rc < 0)
+ return;
+
+ elan_touch_report_data(client, buf);
+}
+
+static irqreturn_t elan_touch_ts_interrupt(int irq, void *dev_id)
+{
+ queue_work(elan_wq, &elan_touch_data.work);
+
+ return IRQ_HANDLED;
+}
+
+static enum hrtimer_restart elan_touch_timer_func(struct hrtimer *timer)
+{
+ queue_work(elan_wq, &elan_touch_data.work);
+ hrtimer_start(&elan_touch_data.timer, ktime_set(0, 12500000),
+ HRTIMER_MODE_REL);
+
+ return HRTIMER_NORESTART;
+}
+
+static int elan_touch_register_interrupt(struct i2c_client *client)
+{
+ int err = 0;
+
+ if (client->irq) {
+ elan_touch_data.use_irq = 1;
+ err =
+ request_irq(client->irq, elan_touch_ts_interrupt,
+ IRQF_TRIGGER_FALLING, ELAN_TS_NAME,
+ &elan_touch_data);
+
+ if (err < 0) {
+ printk("%s(%s): Can't allocate irq %d\n", __FILE__,
+ __func__, client->irq);
+ elan_touch_data.use_irq = 0;
+ }
+ }
+
+ if (!elan_touch_data.use_irq) {
+ hrtimer_init(&elan_touch_data.timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ elan_touch_data.timer.function = elan_touch_timer_func;
+ hrtimer_start(&elan_touch_data.timer, ktime_set(1, 0),
+ HRTIMER_MODE_REL);
+ }
+
+ printk("elan ts starts in %s mode.\n",
+ elan_touch_data.use_irq == 1 ? "interrupt" : "polling");
+
+ return 0;
+}
+
+static int elan_touch_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int err = 0;
+
+ elan_wq = create_singlethread_workqueue("elan_wq");
+ if (!elan_wq) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ elan_touch_data.client = client;
+ strlcpy(client->name, ELAN_TS_NAME, I2C_NAME_SIZE);
+
+ INIT_WORK(&elan_touch_data.work, elan_touch_work_func);
+
+ elan_touch_data.intr_gpio = irq_to_gpio(client->irq);
+ pr_debug("irq_to_gpio irq %d, gpio %d\n", client->irq,
+ elan_touch_data.intr_gpio);
+
+ elan_touch_data.input = input_allocate_device();
+ if (elan_touch_data.input == NULL) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ err = __elan_touch_init(client);
+ if (err < 0) {
+ printk("elan - Read Hello Packet Failed\n");
+ goto fail;
+ }
+
+ elan_touch_data.input->name = ELAN_TS_NAME;
+ elan_touch_data.input->id.bustype = BUS_I2C;
+
+ set_bit(EV_SYN, elan_touch_data.input->evbit);
+
+ set_bit(EV_KEY, elan_touch_data.input->evbit);
+ set_bit(BTN_TOUCH, elan_touch_data.input->keybit);
+ set_bit(BTN_2, elan_touch_data.input->keybit);
+
+ set_bit(EV_ABS, elan_touch_data.input->evbit);
+ set_bit(ABS_X, elan_touch_data.input->absbit);
+ set_bit(ABS_Y, elan_touch_data.input->absbit);
+ set_bit(ABS_HAT0X, elan_touch_data.input->absbit);
+ set_bit(ABS_HAT0Y, elan_touch_data.input->absbit);
+
+ input_set_abs_params(elan_touch_data.input, ABS_X, 0, ELAN_USER_X_MAX,
+ 0, 0);
+ input_set_abs_params(elan_touch_data.input, ABS_Y, 0, ELAN_USER_Y_MAX,
+ 0, 0);
+ input_set_abs_params(elan_touch_data.input, ABS_HAT0X, 0,
+ ELAN_USER_X_MAX, 0, 0);
+ input_set_abs_params(elan_touch_data.input, ABS_HAT0Y, 0,
+ ELAN_USER_Y_MAX, 0, 0);
+
+ err = input_register_device(elan_touch_data.input);
+ if (err < 0) {
+ goto fail;
+ }
+
+ elan_touch_register_interrupt(elan_touch_data.client);
+
+ return 0;
+
+fail:
+ input_free_device(elan_touch_data.input);
+ if (elan_wq)
+ destroy_workqueue(elan_wq);
+ return err;
+}
+
+static int elan_touch_remove(struct i2c_client *client)
+{
+ if (elan_wq)
+ destroy_workqueue(elan_wq);
+
+ input_unregister_device(elan_touch_data.input);
+
+ if (elan_touch_data.use_irq)
+ free_irq(client->irq, client);
+ else
+ hrtimer_cancel(&elan_touch_data.timer);
+ return 0;
+}
+
+/* -------------------------------------------------------------------- */
+static const struct i2c_device_id elan_touch_id[] = {
+ {"elan-touch", 0},
+ {}
+};
+
+static struct i2c_driver elan_touch_driver = {
+ .probe = elan_touch_probe,
+ .remove = elan_touch_remove,
+ .id_table = elan_touch_id,
+ .driver = {
+ .name = "elan-touch",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init elan_touch_init(void)
+{
+ return i2c_add_driver(&elan_touch_driver);
+}
+
+static void __exit elan_touch_exit(void)
+{
+ i2c_del_driver(&elan_touch_driver);
+}
+
+module_init(elan_touch_init);
+module_exit(elan_touch_exit);
+
+MODULE_AUTHOR("Stanley Zeng <stanley.zeng@emc.com.tw>");
+MODULE_DESCRIPTION("ELAN Touch Screen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/max11801_ts.c b/drivers/input/touchscreen/max11801_ts.c
index ca88d6823a98..48ee3600a03b 100755
--- a/drivers/input/touchscreen/max11801_ts.c
+++ b/drivers/input/touchscreen/max11801_ts.c
@@ -2,7 +2,7 @@
* Driver for MAXI MAX11801 - A Resistive touch screen controller with
* i2c interface
*
- * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2011-2012 Freescale Semiconductor, Inc.
* Author: Zhang Jiejing <jiejing.zhang@freescale.com>
*
* Based on mcs5000_ts.c
@@ -38,6 +38,8 @@
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/bitops.h>
+#include <linux/delay.h>
+
/* Register Address define */
#define GENERNAL_STATUS_REG 0x00
@@ -53,13 +55,32 @@
#define AUX_MESURE_CONF_REG 0x0a
#define OP_MODE_CONF_REG 0x0b
+#define Panel_Setup_X (0x69 << 1)
+#define Panel_Setup_Y (0x6b << 1)
+
+#define XY_combined_measurement (0x70 << 1)
+#define X_measurement (0x78 << 1)
+#define Y_measurement (0x7a << 1)
+#define AUX_measurement (0x76 << 1)
+
/* FIFO is found only in max11800 and max11801 */
#define FIFO_RD_CMD (0x50 << 1)
#define MAX11801_FIFO_INT (1 << 2)
#define MAX11801_FIFO_OVERFLOW (1 << 3)
+#define MAX11801_EDGE_INT (1 << 1)
+
+#define FIFO_RD_X_MSB (0x52 << 1)
+#define FIFO_RD_X_LSB (0x53 << 1)
+#define FIFO_RD_Y_MSB (0x54 << 1)
+#define FIFO_RD_Y_LSB (0x55 << 1)
+#define FIFO_RD_AUX_MSB (0x5a << 1)
+#define FIFO_RD_AUX_LSB (0x5b << 1)
#define XY_BUFSIZE 4
#define XY_BUF_OFFSET 4
+#define X_BUFSIZE 2
+#define Y_BUFSIZE 2
+#define AUX_BUFSIZE 2
#define MAX11801_MAX_X 0xfff
#define MAX11801_MAX_Y 0xfff
@@ -83,6 +104,54 @@ struct max11801_data {
struct i2c_client *client;
struct input_dev *input_dev;
};
+struct i2c_client *max11801_client;
+unsigned int max11801_workmode;
+
+static int max11801_dcm_write_command(struct i2c_client *client, int command)
+{
+ return i2c_smbus_write_byte(client, command);
+}
+
+static u32 max11801_dcm_sample_aux(struct i2c_client *client)
+{
+ u8 temp_buf;
+ u8 aux_buf[AUX_BUFSIZE];
+ int ret;
+ int aux = 0;
+ u32 sample_data = 0;
+ /* AUX_measurement*/
+ max11801_dcm_write_command(client, AUX_measurement);
+ mdelay(5);
+ ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_AUX_MSB,
+ 1, &temp_buf);
+ aux_buf[0] = temp_buf;
+ if (ret < 1)
+ printk(KERN_DEBUG "FIFO_RD_AUX_MSB read fails\n");
+ mdelay(5);
+ ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_AUX_LSB,
+ 1, &temp_buf);
+ aux_buf[1] = temp_buf;
+ if (ret < 1)
+ printk(KERN_DEBUG "FIFO_RD_AUX_LSB read fails\n");
+ aux = (aux_buf[0] << 4) +
+ (aux_buf[1] >> 4);
+ /*
+ voltage = (9170*aux)/7371;
+ voltage is (26.2*3150*aux)/(16.2*0xFFF)
+ V(aux)=3150*sample/0xFFF,V(battery)=212*V(aux)/81
+ sample_data = (14840*aux)/7371-1541;
+ */
+ sample_data = (14840*aux)/7371;
+ return sample_data;
+}
+
+u32 max11801_read_adc(void)
+{
+ u32 adc_data;
+ adc_data = max11801_dcm_sample_aux(max11801_client);
+ return adc_data;
+}
+EXPORT_SYMBOL_GPL(max11801_read_adc);
static u8 read_register(struct i2c_client *client, int addr)
{
@@ -109,76 +178,166 @@ static irqreturn_t max11801_ts_interrupt(int irq, void *dev_id)
struct i2c_client *client = data->client;
int status, i, ret;
u8 buf[XY_BUFSIZE];
+ u8 x_buf[X_BUFSIZE];
+ u8 y_buf[Y_BUFSIZE];
+ u8 temp_buf[1];
int x = -1;
int y = -1;
status = read_register(data->client, GENERNAL_STATUS_REG);
-
- if (status & (MAX11801_FIFO_INT | MAX11801_FIFO_OVERFLOW)) {
- status = read_register(data->client, GENERNAL_STATUS_REG);
-
- ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_CMD,
- XY_BUFSIZE, buf);
-
- /*
- * We should get 4 bytes buffer that contains X,Y
- * and event tag
- */
- if (ret < XY_BUFSIZE)
- goto out;
-
- for (i = 0; i < XY_BUFSIZE; i += XY_BUFSIZE / 2) {
- if ((buf[i + 1] & MEASURE_TAG_MASK) == MEASURE_X_TAG)
- x = (buf[i] << XY_BUF_OFFSET) +
- (buf[i + 1] >> XY_BUF_OFFSET);
- else if ((buf[i + 1] & MEASURE_TAG_MASK) == MEASURE_Y_TAG)
- y = (buf[i] << XY_BUF_OFFSET) +
- (buf[i + 1] >> XY_BUF_OFFSET);
- }
-
- if ((buf[1] & EVENT_TAG_MASK) != (buf[3] & EVENT_TAG_MASK))
- goto out;
-
- switch (buf[1] & EVENT_TAG_MASK) {
- case EVENT_INIT:
- /* fall through */
- case EVENT_MIDDLE:
- calibration_pointer(&x, &y);
- input_report_abs(data->input_dev, ABS_X, x);
- input_report_abs(data->input_dev, ABS_Y, y);
- input_event(data->input_dev, EV_KEY, BTN_TOUCH, 1);
- input_sync(data->input_dev);
- break;
-
- case EVENT_RELEASE:
- input_event(data->input_dev, EV_KEY, BTN_TOUCH, 0);
- input_sync(data->input_dev);
- break;
-
- case EVENT_FIFO_END:
- break;
- }
- }
+ if (max11801_workmode == 0) {
+ if (status & (MAX11801_FIFO_INT | MAX11801_FIFO_OVERFLOW)) {
+ status = read_register(data->client, GENERNAL_STATUS_REG);
+
+ ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_CMD,
+ XY_BUFSIZE, buf);
+
+ /*
+ * We should get 4 bytes buffer that contains X,Y
+ * and event tag
+ */
+ if (ret < XY_BUFSIZE)
+ goto out;
+
+ for (i = 0; i < XY_BUFSIZE; i += XY_BUFSIZE / 2) {
+ if ((buf[i + 1] & MEASURE_TAG_MASK) == MEASURE_X_TAG)
+ x = (buf[i] << XY_BUF_OFFSET) +
+ (buf[i + 1] >> XY_BUF_OFFSET);
+ else if ((buf[i + 1] & MEASURE_TAG_MASK) == MEASURE_Y_TAG)
+ y = (buf[i] << XY_BUF_OFFSET) +
+ (buf[i + 1] >> XY_BUF_OFFSET);
+ }
+
+ if ((buf[1] & EVENT_TAG_MASK) != (buf[3] & EVENT_TAG_MASK))
+ goto out;
+
+ switch (buf[1] & EVENT_TAG_MASK) {
+ case EVENT_INIT:
+ /* fall through */
+ case EVENT_MIDDLE:
+ calibration_pointer(&x, &y);
+ input_report_abs(data->input_dev, ABS_X, x);
+ input_report_abs(data->input_dev, ABS_Y, y);
+ input_event(data->input_dev, EV_KEY, BTN_TOUCH, 1);
+ input_sync(data->input_dev);
+ break;
+
+ case EVENT_RELEASE:
+ input_event(data->input_dev, EV_KEY, BTN_TOUCH, 0);
+ input_sync(data->input_dev);
+ break;
+
+ case EVENT_FIFO_END:
+ break;
+ }
+ }
out:
- return IRQ_HANDLED;
+return IRQ_HANDLED;
+ }
+ else if (max11801_workmode == 1) {
+ if (status & (MAX11801_EDGE_INT)) {
+ status = read_register(data->client, GENERNAL_STATUS_REG);
+
+ /* X = panel setup*/
+ max11801_dcm_write_command(client, Panel_Setup_X);
+ /* X_measurement*/
+ max11801_dcm_write_command(client, X_measurement);
+ ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_X_MSB,
+ 1, temp_buf);
+ x_buf[0] = temp_buf[0];
+ if (ret < 1)
+ goto out2;
+ ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_X_LSB,
+ 1, temp_buf);
+ x_buf[1] = temp_buf[0];
+ if (ret < 1)
+ goto out2;
+ /* Y = panel setup*/
+ max11801_dcm_write_command(client, Panel_Setup_Y);
+ /* Y_measurement*/
+ max11801_dcm_write_command(client, Y_measurement);
+ ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_Y_MSB,
+ 1, temp_buf);
+ y_buf[0] = temp_buf[0];
+ if (ret < 1)
+ goto out2;
+ ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_Y_LSB,
+ 1, temp_buf);
+ y_buf[1] = temp_buf[0];
+ if (ret < 1)
+ goto out2;
+
+ if ((x_buf[1] & MEASURE_TAG_MASK) == MEASURE_X_TAG)
+ x = (x_buf[0] << XY_BUF_OFFSET) +
+ (x_buf[1] >> XY_BUF_OFFSET);
+ if ((y_buf[1] & MEASURE_TAG_MASK) == MEASURE_Y_TAG)
+ y = (y_buf[0] << XY_BUF_OFFSET) +
+ (y_buf[1] >> XY_BUF_OFFSET);
+
+ if ((x_buf[1] & EVENT_TAG_MASK) != (y_buf[1] & EVENT_TAG_MASK))
+ goto out2;
+
+ switch (x_buf[1] & EVENT_TAG_MASK) {
+ case EVENT_INIT:
+ /* fall through */
+ case EVENT_MIDDLE:
+ calibration_pointer(&x, &y);
+ input_report_abs(data->input_dev, ABS_X, x);
+ input_report_abs(data->input_dev, ABS_Y, y);
+ input_event(data->input_dev, EV_KEY, BTN_TOUCH, 1);
+ input_sync(data->input_dev);
+ break;
+
+ case EVENT_RELEASE:
+ input_event(data->input_dev, EV_KEY, BTN_TOUCH, 0);
+ input_sync(data->input_dev);
+ break;
+
+ case EVENT_FIFO_END:
+ break;
+ }
+ }
+ }
+out2:
+return IRQ_HANDLED;
}
static void __devinit max11801_ts_phy_init(struct max11801_data *data)
{
struct i2c_client *client = data->client;
-
- /* Average X,Y, take 16 samples, average eight media sample */
- max11801_write_reg(client, MESURE_AVER_CONF_REG, 0xff);
- /* X,Y panel setup time set to 20us */
- max11801_write_reg(client, PANEL_SETUPTIME_CONF_REG, 0x11);
- /* Rough pullup time (2uS), Fine pullup time (10us) */
- max11801_write_reg(client, TOUCH_DETECT_PULLUP_CONF_REG, 0x10);
- /* Auto mode init period = 5ms , scan period = 5ms*/
- max11801_write_reg(client, AUTO_MODE_TIME_CONF_REG, 0xaa);
- /* Aperture X,Y set to +- 4LSB */
- max11801_write_reg(client, APERTURE_CONF_REG, 0x33);
- /* Enable Power, enable Automode, enable Aperture, enable Average X,Y */
- max11801_write_reg(client, OP_MODE_CONF_REG, 0x36);
+ max11801_client = client;
+ if (max11801_workmode == 0) {
+ /* Average X,Y, take 16 samples, average eight media sample */
+ max11801_write_reg(client, MESURE_AVER_CONF_REG, 0xff);
+ /* X,Y panel setup time set to 20us */
+ max11801_write_reg(client, PANEL_SETUPTIME_CONF_REG, 0x11);
+ /* Rough pullup time (2uS), Fine pullup time (10us) */
+ max11801_write_reg(client, TOUCH_DETECT_PULLUP_CONF_REG, 0x10);
+ /* Auto mode init period = 5ms , scan period = 5ms*/
+ max11801_write_reg(client, AUTO_MODE_TIME_CONF_REG, 0xaa);
+ /* Aperture X,Y set to +- 4LSB */
+ max11801_write_reg(client, APERTURE_CONF_REG, 0x33);
+ /* Enable Power, enable Automode, enable Aperture, enable Average X,Y */
+ max11801_write_reg(client, OP_MODE_CONF_REG, 0x36);
+ }
+ if (max11801_workmode == 1) {
+ /* Average X,Y, take 16 samples, average eight media sample */
+ max11801_write_reg(client, MESURE_AVER_CONF_REG, 0xff);
+ /* X,Y panel setup time set to 20us */
+ max11801_write_reg(client, PANEL_SETUPTIME_CONF_REG, 0x11);
+ /* Rough pullup time (2uS), Fine pullup time (10us) */
+ max11801_write_reg(client, TOUCH_DETECT_PULLUP_CONF_REG, 0x10);
+ /* Auto mode init period = 5ms , scan period = 5ms*/
+ max11801_write_reg(client, AUTO_MODE_TIME_CONF_REG, 0xaa);
+ /* Aperture X,Y set to +- 4LSB */
+ max11801_write_reg(client, APERTURE_CONF_REG, 0x33);
+ /* Enable Power, enable Direct conversion mode , enable Aperture, enable Average X,Y */
+ max11801_write_reg(client, OP_MODE_CONF_REG, 0x16);
+ /* Delay initial=1ms, Sampling time 2us ,Averaging sample depth 2 samples, Resolution 12bit */
+ max11801_write_reg(client, AUX_MESURE_CONF_REG, 0x76);
+ /* Use edge interrupt with direct conversion mode */
+ max11801_write_reg(client, GENERNAL_CONF_REG, 0xf3);
+ }
}
static int __devinit max11801_ts_probe(struct i2c_client *client,
@@ -195,7 +354,7 @@ static int __devinit max11801_ts_probe(struct i2c_client *client,
error = -ENOMEM;
goto err_free_mem;
}
-
+ max11801_workmode = *(int *)(client->dev).platform_data;
data->client = client;
data->input_dev = input_dev;
diff --git a/drivers/media/video/mxc/capture/ipu_prp_enc.c b/drivers/media/video/mxc/capture/ipu_prp_enc.c
index 6710cba5d817..02515b35ba03 100644
--- a/drivers/media/video/mxc/capture/ipu_prp_enc.c
+++ b/drivers/media/video/mxc/capture/ipu_prp_enc.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2004-2012 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
@@ -170,7 +170,8 @@ static int prp_enc_setup(cam_data *cam)
return err;
}
- ipu_csi_enable_mclk_if(cam->ipu, CSI_MCLK_ENC, cam->csi, true, true);
+ ipu_csi_enable_mclk_if(cam->ipu, CSI_MCLK_ENC,
+ cam->mclk_source, true, true);
grotation = cam->rotation;
if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
diff --git a/drivers/media/video/mxc/capture/mxc_v4l2_capture.c b/drivers/media/video/mxc/capture/mxc_v4l2_capture.c
index 7713b3d80846..c2bda758a362 100644
--- a/drivers/media/video/mxc/capture/mxc_v4l2_capture.c
+++ b/drivers/media/video/mxc/capture/mxc_v4l2_capture.c
@@ -32,19 +32,19 @@
#include <linux/types.h>
#include <linux/fb.h>
#include <linux/dma-mapping.h>
+#include <linux/delay.h>
#include <linux/mxcfb.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-int-device.h>
+#include <linux/fsl_devices.h>
#include "mxc_v4l2_capture.h"
#include "ipu_prp_sw.h"
#define init_MUTEX(sem) sema_init(sem, 1)
#define MXC_SENSOR_NUM 2
-static int sensor_index;
-static int video_nr = -1, local_buf_num;
-static cam_data *g_cam;
+static int video_nr = -1;
/*! This data is used for the output to the display. */
#define MXC_V4L2_CAPTURE_NUM_OUTPUTS 3
@@ -404,7 +404,7 @@ static int mxc_streamon(cam_data *cam)
}
cam->ping_pong_csi = 0;
- local_buf_num = 0;
+ cam->local_buf_num = 0;
if (cam->enc_update_eba) {
frame =
list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue);
@@ -1229,7 +1229,7 @@ static int mxc_v4l2_s_ctrl(cam_data *cam, struct v4l2_control *c)
case V4L2_CID_MXC_SWITCH_CAM:
if (cam->sensor != cam->all_sensors[c->value]) {
/* power down other cameraes before enable new one */
- for (i = 0; i < sensor_index; i++) {
+ for (i = 0; i < cam->sensor_index; i++) {
if (i != c->value) {
vidioc_int_dev_exit(cam->all_sensors[i]);
vidioc_int_s_power(cam->all_sensors[i], 0);
@@ -1684,12 +1684,13 @@ static int mxc_v4l_open(struct file *file)
cam_fmt.fmt.pix.pixelformat,
csi_param);
- ipu_csi_enable_mclk_if(cam->ipu, CSI_MCLK_I2C, cam->csi,
+ ipu_csi_enable_mclk_if(cam->ipu, CSI_MCLK_I2C, cam->mclk_source,
true, true);
vidioc_int_s_power(cam->sensor, 1);
+ msleep(1);
vidioc_int_init(cam->sensor);
vidioc_int_dev_init(cam->sensor);
-}
+ }
file->private_data = dev;
@@ -2519,7 +2520,7 @@ static void camera_callback(u32 mask, void *dev)
struct mxc_v4l_frame,
queue);
- if (done_frame->ipu_buf_num != local_buf_num)
+ if (done_frame->ipu_buf_num != cam->local_buf_num)
goto next;
/*
@@ -2556,7 +2557,7 @@ next:
list_del(cam->ready_q.next);
list_add_tail(&ready_frame->queue,
&cam->working_q);
- ready_frame->ipu_buf_num = local_buf_num;
+ ready_frame->ipu_buf_num = cam->local_buf_num;
}
} else {
if (cam->enc_update_eba)
@@ -2565,7 +2566,7 @@ next:
&cam->ping_pong_csi);
}
- local_buf_num = (local_buf_num == 0) ? 1 : 0;
+ cam->local_buf_num = (cam->local_buf_num == 0) ? 1 : 0;
return;
}
@@ -2579,11 +2580,13 @@ next:
*/
static void init_camera_struct(cam_data *cam, struct platform_device *pdev)
{
+ struct fsl_mxc_capture_platform_data *pdata = pdev->dev.platform_data;
+
pr_debug("In MVC: init_camera_struct\n");
/* Default everything to 0 */
memset(cam, 0, sizeof(cam_data));
- cam->ipu = ipu_get_soc(0);
+ cam->ipu = ipu_get_soc(pdata->ipu);
if (cam->ipu == NULL)
pr_err("ERROR: v4l2 capture: failed to get ipu\n");
else if (cam->ipu == ERR_PTR(-ENODEV))
@@ -2640,8 +2643,8 @@ static void init_camera_struct(cam_data *cam, struct platform_device *pdev)
cam->win.w.left = 0;
cam->win.w.top = 0;
- cam->csi = 0; /* Need to determine how to set this correctly with
- * multiple video input devices. */
+ cam->csi = pdata->csi;
+ cam->mclk_source = pdata->mclk_source;
cam->enc_callback = camera_callback;
init_waitqueue_head(&cam->power_queue);
@@ -2655,6 +2658,12 @@ static void init_camera_struct(cam_data *cam, struct platform_device *pdev)
pr_err("ERROR: v4l2 capture: Allocate dummy frame "
"failed.\n");
cam->dummy_frame.buffer.length = SZ_8M;
+
+ cam->self = kmalloc(sizeof(struct v4l2_int_device), GFP_KERNEL);
+ cam->self->module = THIS_MODULE;
+ sprintf(cam->self->name, "mxc_v4l2_cap%d", cam->csi);
+ cam->self->type = v4l2_int_type_master;
+ cam->self->u.master = &mxc_v4l2_master;
}
static ssize_t show_streaming(struct device *dev,
@@ -2662,9 +2671,9 @@ static ssize_t show_streaming(struct device *dev,
{
struct video_device *video_dev = container_of(dev,
struct video_device, dev);
- cam_data *g_cam = video_get_drvdata(video_dev);
+ cam_data *cam = video_get_drvdata(video_dev);
- if (g_cam->capture_on)
+ if (cam->capture_on)
return sprintf(buf, "stream on\n");
else
return sprintf(buf, "stream off\n");
@@ -2676,9 +2685,9 @@ static ssize_t show_overlay(struct device *dev,
{
struct video_device *video_dev = container_of(dev,
struct video_device, dev);
- cam_data *g_cam = video_get_drvdata(video_dev);
+ cam_data *cam = video_get_drvdata(video_dev);
- if (g_cam->overlay_on)
+ if (cam->overlay_on)
return sprintf(buf, "overlay on\n");
else
return sprintf(buf, "overlay off\n");
@@ -2695,38 +2704,38 @@ static DEVICE_ATTR(fsl_v4l2_overlay_property, S_IRUGO, show_overlay, NULL);
*/
static int mxc_v4l2_probe(struct platform_device *pdev)
{
- /* Create g_cam and initialize it. */
- g_cam = kmalloc(sizeof(cam_data), GFP_KERNEL);
- if (g_cam == NULL) {
+ /* Create cam and initialize it. */
+ cam_data *cam = kmalloc(sizeof(cam_data), GFP_KERNEL);
+ if (cam == NULL) {
pr_err("ERROR: v4l2 capture: failed to register camera\n");
return -1;
}
- init_camera_struct(g_cam, pdev);
+ init_camera_struct(cam, pdev);
pdev->dev.release = camera_platform_release;
/* Set up the v4l2 device and register it*/
- mxc_v4l2_int_device.priv = g_cam;
+ cam->self->priv = cam;
/* This function contains a bug that won't let this be rmmod'd. */
- v4l2_int_device_register(&mxc_v4l2_int_device);
+ v4l2_int_device_register(cam->self);
/* register v4l video device */
- if (video_register_device(g_cam->video_dev, VFL_TYPE_GRABBER, video_nr)
+ if (video_register_device(cam->video_dev, VFL_TYPE_GRABBER, video_nr)
== -1) {
- kfree(g_cam);
- g_cam = NULL;
+ kfree(cam);
+ cam = NULL;
pr_err("ERROR: v4l2 capture: video_register_device failed\n");
return -1;
}
pr_debug(" Video device registered: %s #%d\n",
- g_cam->video_dev->name, g_cam->video_dev->minor);
+ cam->video_dev->name, cam->video_dev->minor);
- if (device_create_file(&g_cam->video_dev->dev,
+ if (device_create_file(&cam->video_dev->dev,
&dev_attr_fsl_v4l2_capture_property))
dev_err(&pdev->dev, "Error on creating sysfs file"
" for capture\n");
- if (device_create_file(&g_cam->video_dev->dev,
+ if (device_create_file(&cam->video_dev->dev,
&dev_attr_fsl_v4l2_overlay_property))
dev_err(&pdev->dev, "Error on creating sysfs file"
" for overlay\n");
@@ -2744,31 +2753,31 @@ static int mxc_v4l2_probe(struct platform_device *pdev)
*/
static int mxc_v4l2_remove(struct platform_device *pdev)
{
+ cam_data *cam = (cam_data *)platform_get_drvdata(pdev);
- if (g_cam->dummy_frame.vaddress != 0) {
- dma_free_coherent(0, g_cam->dummy_frame.buffer.length,
- g_cam->dummy_frame.vaddress,
- g_cam->dummy_frame.paddress);
- g_cam->dummy_frame.vaddress = 0;
+ if (cam->dummy_frame.vaddress != 0) {
+ dma_free_coherent(0, cam->dummy_frame.buffer.length,
+ cam->dummy_frame.vaddress,
+ cam->dummy_frame.paddress);
+ cam->dummy_frame.vaddress = 0;
}
- if (g_cam->open_count) {
+ if (cam->open_count) {
pr_err("ERROR: v4l2 capture:camera open "
"-- setting ops to NULL\n");
return -EBUSY;
} else {
- device_remove_file(&g_cam->video_dev->dev,
+ device_remove_file(&cam->video_dev->dev,
&dev_attr_fsl_v4l2_capture_property);
- device_remove_file(&g_cam->video_dev->dev,
+ device_remove_file(&cam->video_dev->dev,
&dev_attr_fsl_v4l2_overlay_property);
pr_info("V4L2 freeing image input device\n");
v4l2_int_device_unregister(&mxc_v4l2_int_device);
- video_unregister_device(g_cam->video_dev);
+ video_unregister_device(cam->video_dev);
- mxc_free_frame_buf(g_cam);
- kfree(g_cam);
- g_cam = NULL;
+ mxc_free_frame_buf(cam);
+ kfree(cam);
}
pr_info("V4L2 unregistering video\n");
@@ -2865,6 +2874,7 @@ static int mxc_v4l2_master_attach(struct v4l2_int_device *slave)
cam_data *cam = slave->u.slave->master->priv;
struct v4l2_format cam_fmt;
int i;
+ struct sensor_data *sdata = slave->priv;
pr_debug("In MVC: mxc_v4l2_master_attach\n");
pr_debug(" slave.name = %s\n", slave->name);
@@ -2875,17 +2885,22 @@ static int mxc_v4l2_master_attach(struct v4l2_int_device *slave)
return -1;
}
+ if (sdata->csi != cam->csi) {
+ pr_debug("%s: csi doesn't match\n", __func__);
+ return -1;
+ }
+
cam->sensor = slave;
- if (sensor_index < MXC_SENSOR_NUM) {
- cam->all_sensors[sensor_index] = slave;
- sensor_index++;
+ if (cam->sensor_index < MXC_SENSOR_NUM) {
+ cam->all_sensors[cam->sensor_index] = slave;
+ cam->sensor_index++;
} else {
pr_err("ERROR: v4l2 capture: slave number exceeds the maximum.\n");
return -1;
}
- for (i = 0; i < sensor_index; i++) {
+ for (i = 0; i < cam->sensor_index; i++) {
vidioc_int_dev_exit(cam->all_sensors[i]);
vidioc_int_s_power(cam->all_sensors[i], 0);
}
@@ -2937,23 +2952,23 @@ static void mxc_v4l2_master_detach(struct v4l2_int_device *slave)
pr_debug("In MVC:mxc_v4l2_master_detach\n");
- if (sensor_index > 1) {
- for (i = 0; i < sensor_index; i++) {
+ if (cam->sensor_index > 1) {
+ for (i = 0; i < cam->sensor_index; i++) {
if (cam->all_sensors[i] != slave)
continue;
/* Move all the sensors behind this
* sensor one step forward
*/
- for (; i < sensor_index - 1; i++)
+ for (; i < cam->sensor_index - 1; i++)
cam->all_sensors[i] = cam->all_sensors[i+1];
break;
}
/* Point current sensor to the last one */
- cam->sensor = cam->all_sensors[sensor_index - 2];
+ cam->sensor = cam->all_sensors[cam->sensor_index - 2];
} else
cam->sensor = NULL;
- sensor_index--;
+ cam->sensor_index--;
vidioc_int_dev_exit(slave);
}
diff --git a/drivers/media/video/mxc/capture/mxc_v4l2_capture.h b/drivers/media/video/mxc/capture/mxc_v4l2_capture.h
index 5b7b0bfe7706..dcb90a9f4ce5 100644
--- a/drivers/media/video/mxc/capture/mxc_v4l2_capture.h
+++ b/drivers/media/video/mxc/capture/mxc_v4l2_capture.h
@@ -34,6 +34,8 @@
#include <mach/ipu-v3.h>
#include <media/v4l2-dev.h>
+#include <media/v4l2-int-device.h>
+
#define FRAME_NUM 10
@@ -190,12 +192,17 @@ typedef struct _cam_data {
bool low_power;
wait_queue_head_t power_queue;
unsigned int csi;
+ u8 mclk_source;
int current_input;
+ int local_buf_num;
+
/* camera sensor interface */
struct camera_sensor *cam_sensor; /* old version */
struct v4l2_int_device *all_sensors[2];
struct v4l2_int_device *sensor;
+ struct v4l2_int_device *self;
+ int sensor_index;
void *ipu;
} cam_data;
@@ -218,6 +225,7 @@ struct sensor_data {
int ae_mode;
u32 mclk;
+ u8 mclk_source;
int csi;
void (*io_init)(void);
diff --git a/drivers/media/video/mxc/capture/ov3640.c b/drivers/media/video/mxc/capture/ov3640.c
index b1d1abb884d9..69a3ad43b22c 100644
--- a/drivers/media/video/mxc/capture/ov3640.c
+++ b/drivers/media/video/mxc/capture/ov3640.c
@@ -1354,7 +1354,7 @@ static int ioctl_dev_init(struct v4l2_int_device *s)
ov3640_data.mclk = tgt_xclk;
pr_debug(" Setting mclk to %d MHz\n", tgt_xclk / 1000000);
- set_mclk_rate(&ov3640_data.mclk, ov3640_data.csi);
+ set_mclk_rate(&ov3640_data.mclk, ov3640_data.mclk_source);
/* Default camera frame rate is set in probe */
tgt_fps = sensor->streamcap.timeperframe.denominator /
@@ -1442,6 +1442,7 @@ static int ov3640_probe(struct i2c_client *client,
memset(&ov3640_data, 0, sizeof(ov3640_data));
ov3640_data.mclk = 24000000; /* 6 - 54 MHz, typical 24MHz */
ov3640_data.mclk = plat_data->mclk;
+ ov3640_data.mclk_source = plat_data->mclk_source;
ov3640_data.csi = plat_data->csi;
ov3640_data.io_init = plat_data->io_init;
diff --git a/drivers/media/video/mxc/capture/ov5640.c b/drivers/media/video/mxc/capture/ov5640.c
index f9087499b5f1..c2dfe314c65d 100644
--- a/drivers/media/video/mxc/capture/ov5640.c
+++ b/drivers/media/video/mxc/capture/ov5640.c
@@ -1309,7 +1309,7 @@ static int ioctl_dev_init(struct v4l2_int_device *s)
ov5640_data.mclk = tgt_xclk;
pr_debug(" Setting mclk to %d MHz\n", tgt_xclk / 1000000);
- set_mclk_rate(&ov5640_data.mclk, ov5640_data.csi);
+ set_mclk_rate(&ov5640_data.mclk, ov5640_data.mclk_source);
/* Default camera frame rate is set in probe */
tgt_fps = sensor->streamcap.timeperframe.denominator /
@@ -1397,6 +1397,7 @@ static int ov5640_probe(struct i2c_client *client,
memset(&ov5640_data, 0, sizeof(ov5640_data));
ov5640_data.mclk = 24000000; /* 6 - 54 MHz, typical 24MHz */
ov5640_data.mclk = plat_data->mclk;
+ ov5640_data.mclk_source = plat_data->mclk_source;
ov5640_data.csi = plat_data->csi;
ov5640_data.io_init = plat_data->io_init;
diff --git a/drivers/media/video/mxc/capture/ov5640_mipi.c b/drivers/media/video/mxc/capture/ov5640_mipi.c
index 2d9d83b7b139..9ffca952cc29 100644
--- a/drivers/media/video/mxc/capture/ov5640_mipi.c
+++ b/drivers/media/video/mxc/capture/ov5640_mipi.c
@@ -195,7 +195,7 @@ static struct reg_value ov5640_setting_30fps_VGA_640_480[] = {
};
static struct reg_value ov5640_setting_15fps_VGA_640_480[] = {
- {0x3035, 0x24, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+ {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
@@ -237,7 +237,7 @@ static struct reg_value ov5640_setting_30fps_QVGA_320_240[] = {
};
static struct reg_value ov5640_setting_15fps_QVGA_320_240[] = {
- {0x3035, 0x24, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+ {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
@@ -909,6 +909,15 @@ bool binning_on(void)
return false;
}
+static void ov5640_set_virtual_channel(int channel)
+{
+ u8 channel_id;
+
+ ov5640_read_reg(0x4814, &channel_id);
+ channel_id &= ~(3 << 6);
+ ov5640_write_reg(0x4814, channel_id | (channel << 6));
+}
+
static int ov5640_init_mode(enum ov5640_frame_rate frame_rate,
enum ov5640_mode mode)
{
@@ -1164,6 +1173,7 @@ static int ov5640_init_mode(enum ov5640_frame_rate frame_rate,
OV5640_set_AE_target(AE_Target);
OV5640_get_light_frequency();
OV5640_set_bandingfilter();
+ ov5640_set_virtual_channel(ov5640_data.csi);
if (mipi_csi2_info) {
unsigned int i;
@@ -1604,7 +1614,7 @@ static int ioctl_dev_init(struct v4l2_int_device *s)
ov5640_data.mclk = tgt_xclk;
pr_debug(" Setting mclk to %d MHz\n", tgt_xclk / 1000000);
- set_mclk_rate(&ov5640_data.mclk, ov5640_data.csi);
+ set_mclk_rate(&ov5640_data.mclk, ov5640_data.mclk_source);
/* Default camera frame rate is set in probe */
tgt_fps = sensor->streamcap.timeperframe.denominator /
@@ -1712,6 +1722,7 @@ static int ov5640_probe(struct i2c_client *client,
memset(&ov5640_data, 0, sizeof(ov5640_data));
ov5640_data.mclk = 24000000; /* 6 - 54 MHz, typical 24MHz */
ov5640_data.mclk = plat_data->mclk;
+ ov5640_data.mclk_source = plat_data->mclk_source;
ov5640_data.csi = plat_data->csi;
ov5640_data.io_init = plat_data->io_init;
diff --git a/drivers/media/video/mxc/capture/ov5642.c b/drivers/media/video/mxc/capture/ov5642.c
index f8f8a6c12ab4..58ee92eef678 100644
--- a/drivers/media/video/mxc/capture/ov5642.c
+++ b/drivers/media/video/mxc/capture/ov5642.c
@@ -279,7 +279,7 @@ static struct reg_value ov5642_initial_setting[] = {
{0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
{0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
{0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
- {0x302b, 0x00, 0, 0},
+ {0x302b, 0x00, 0, 300},
};
static struct reg_value ov5642_setting_15fps_QCIF_176_144[] = {
@@ -3255,7 +3255,7 @@ static int ioctl_dev_init(struct v4l2_int_device *s)
ov5642_data.mclk = tgt_xclk;
pr_debug(" Setting mclk to %d MHz\n", tgt_xclk / 1000000);
- set_mclk_rate(&ov5642_data.mclk, ov5642_data.csi);
+ set_mclk_rate(&ov5642_data.mclk, ov5642_data.mclk_source);
/* Default camera frame rate is set in probe */
tgt_fps = sensor->streamcap.timeperframe.denominator /
@@ -3368,6 +3368,7 @@ static int ov5642_probe(struct i2c_client *client,
memset(&ov5642_data, 0, sizeof(ov5642_data));
ov5642_data.mclk = 24000000; /* 6 - 54 MHz, typical 24MHz */
ov5642_data.mclk = plat_data->mclk;
+ ov5642_data.mclk_source = plat_data->mclk_source;
ov5642_data.csi = plat_data->csi;
ov5642_data.io_init = plat_data->io_init;
diff --git a/drivers/media/video/mxc/capture/ov8820_mipi.c b/drivers/media/video/mxc/capture/ov8820_mipi.c
index 4c2a49d8bef5..6fddbb03fc0b 100644
--- a/drivers/media/video/mxc/capture/ov8820_mipi.c
+++ b/drivers/media/video/mxc/capture/ov8820_mipi.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
@@ -92,6 +92,7 @@ struct sensor {
int ae_mode;
u32 mclk;
+ u8 mclk_source;
int csi;
} ov8820_data;
@@ -762,7 +763,7 @@ static int ioctl_dev_init(struct v4l2_int_device *s)
ov8820_data.mclk = tgt_xclk;
pr_debug(" Setting mclk to %d MHz\n", tgt_xclk / 1000000);
- set_mclk_rate(&ov8820_data.mclk, ov8820_data.csi);
+ set_mclk_rate(&ov8820_data.mclk, ov8820_data.mclk_source);
/* Default camera frame rate is set in probe */
tgt_fps = sensor->streamcap.timeperframe.denominator /
@@ -871,6 +872,7 @@ static int ov8820_probe(struct i2c_client *client,
memset(&ov8820_data, 0, sizeof(ov8820_data));
ov8820_data.mclk = 24000000; /* 6 - 54 MHz, typical 24MHz */
ov8820_data.mclk = plat_data->mclk;
+ ov8820_data.mclk_source = plat_data->mclk_source;
ov8820_data.csi = plat_data->csi;
ov8820_data.i2c_client = client;
diff --git a/drivers/media/video/mxc/output/mxc_vout.c b/drivers/media/video/mxc/output/mxc_vout.c
index aebe57fda0c5..e3e9483ac70c 100644
--- a/drivers/media/video/mxc/output/mxc_vout.c
+++ b/drivers/media/video/mxc/output/mxc_vout.c
@@ -1675,16 +1675,18 @@ static void release_disp_output(struct mxc_vout_output *vout)
set_window_position(vout, &pos);
if (get_ipu_channel(fbi) == MEM_BG_SYNC) {
- console_lock();
- fbi->fix.smem_start = vout->fb_smem_start;
- fbi->fix.smem_len = vout->fb_smem_len;
- vout->fb_var.activate |= FB_ACTIVATE_FORCE;
- fbi->flags |= FBINFO_MISC_USEREVENT;
- ret = fb_set_var(fbi, &vout->fb_var);
- fbi->flags &= ~FBINFO_MISC_USEREVENT;
- console_unlock();
- if (ret < 0)
- v4l2_err(vout->vfd->v4l2_dev, "ERR: fb_set_var.\n");
+ if ((vout->fb_smem_len != 0) && (vout->fb_smem_start != 0)) {
+ console_lock();
+ fbi->fix.smem_start = vout->fb_smem_start;
+ fbi->fix.smem_len = vout->fb_smem_len;
+ vout->fb_var.activate |= FB_ACTIVATE_FORCE;
+ fbi->flags |= FBINFO_MISC_USEREVENT;
+ ret = fb_set_var(fbi, &vout->fb_var);
+ fbi->flags &= ~FBINFO_MISC_USEREVENT;
+ console_unlock();
+ if (ret < 0)
+ v4l2_err(vout->vfd->v4l2_dev, "ERR: fb_set_var.\n");
+ }
console_lock();
fbi->flags |= FBINFO_MISC_USEREVENT;
fb_blank(fbi, FB_BLANK_UNBLANK);
diff --git a/drivers/mfd/pfuze-core.c b/drivers/mfd/pfuze-core.c
index 96b62924adec..9c7a988cf89b 100644
--- a/drivers/mfd/pfuze-core.c
+++ b/drivers/mfd/pfuze-core.c
@@ -501,9 +501,10 @@ static int pfuze_probe(struct i2c_client *client,
if (ret)
goto err_mask;
}
- ret = request_threaded_irq(client->irq, NULL, pfuze_irq_thread,
- IRQF_ONESHOT | IRQF_TRIGGER_LOW, "pfuze",
- mc_pfuze);
+ if (client->irq)
+ ret = request_threaded_irq(client->irq, NULL, pfuze_irq_thread,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW, "pfuze",
+ mc_pfuze);
if (ret) {
err_mask:
err_revision:
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 17773740d014..d20fc54eebf0 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -108,6 +108,8 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = pltfm_host->priv;
+ struct esdhc_platform_data *boarddata
+ = host->mmc->parent->platform_data;
/* fake CARD_PRESENT flag on mx25/35 */
u32 val = readl(host->ioaddr + reg);
@@ -127,18 +129,18 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
val |= (fsl_prss & 0x00800000) << 1;
}
- if (unlikely((reg == SDHCI_PRESENT_STATE)
- && (imx_data->flags & ESDHC_FLAG_GPIO_FOR_CD_WP))) {
- struct esdhc_platform_data *boarddata =
- host->mmc->parent->platform_data;
-
- if (boarddata && gpio_is_valid(boarddata->cd_gpio)
- && gpio_get_value(boarddata->cd_gpio))
- /* no card, if a valid gpio says so... */
- val &= ~SDHCI_CARD_PRESENT;
- else
- /* ... in all other cases assume card is present */
+ if (unlikely(reg == SDHCI_PRESENT_STATE)) {
+ if (boarddata && boarddata->always_present)
val |= SDHCI_CARD_PRESENT;
+ else if (imx_data->flags & ESDHC_FLAG_GPIO_FOR_CD_WP) {
+ if (boarddata && gpio_is_valid(boarddata->cd_gpio)
+ && gpio_get_value(boarddata->cd_gpio))
+ /* no card, if a valid gpio says so */
+ val &= ~SDHCI_CARD_PRESENT;
+ else
+ /* in all other cases assume card is present */
+ val |= SDHCI_CARD_PRESENT;
+ }
}
if (reg == SDHCI_INT_STATUS && cpu_is_mx6()
@@ -756,7 +758,7 @@ static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pd
if (boarddata) {
/* Device is always present, e.x, populated emmc device */
if (boarddata->always_present) {
- imx_data->flags |= ESDHC_FLAG_GPIO_FOR_CD_WP;
+ /* remove BROKEN_CD to disable card polling */
host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
if (host->clk_mgr_en)
clk_disable(pltfm_host->clk);
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 1aedbe436c25..2f200b8aa49b 100755
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -35,6 +35,11 @@
#define DBG(f, x...) \
pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x)
+#if defined(CONFIG_LEDS_CLASS) || (defined(CONFIG_LEDS_CLASS_MODULE) && \
+ defined(CONFIG_MMC_SDHCI_MODULE))
+/* FIXME: temp workaround for calling clk_enable in an interrupt context */
+#define SDHCI_USE_LEDS_CLASS_BROKEN
+#endif
#define MAX_TUNING_LOOP 40
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
index 1c273e4eb2b4..75c2e618b28a 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
@@ -152,7 +152,7 @@ static int enable_ddr_onfi(struct gpmi_nand_data *this)
struct mtd_info *mtd = &this->mtd;
int saved_chip_number = 0;
uint8_t device_feature[FEATURE_SIZE];
- int mode = 0;/* there is 5 mode available, default is 0 */
+ int mode = 5;/* there is 5 mode available, default is 0 */
saved_chip_number = this->current_chip;
nand->select_chip(mtd, 0);
@@ -178,14 +178,16 @@ static int enable_ddr_onfi(struct gpmi_nand_data *this)
/* [3] about the clock, pay attention! */
nand->select_chip(mtd, saved_chip_number);
{
- struct clk *pll1;
- pll1 = clk_get(NULL, "pll1_main_clk");
- if (IS_ERR(pll1)) {
- printk(KERN_INFO "No PLL1 clock\n");
+ struct clk *enfc_clk;
+ enfc_clk = clk_get(NULL, "enfc_clk");
+ if (IS_ERR(enfc_clk)) {
+ printk(KERN_INFO "No enfc_clk clock\n");
return -EINVAL;
}
- clk_set_parent(resources->clock, pll1);
- clk_set_rate(resources->clock, 20000000);
+ clk_set_parent(resources->clock, enfc_clk);
+ clk_set_rate(enfc_clk, \
+ enfc_clk->round_rate(enfc_clk, 100000000));
+ clk_set_rate(resources->clock, 100000000);
}
nand->select_chip(mtd, 0);
@@ -258,18 +260,20 @@ static int enable_ddr_toggle(struct gpmi_nand_data *this)
/* [3] about the clock, pay attention! */
nand->select_chip(mtd, saved_chip_number);
{
- struct clk *pll1;
+ struct clk *enfc_clk;
unsigned long rate;
- pll1 = clk_get(NULL, "pll1_main_clk");
- if (IS_ERR(pll1)) {
- printk(KERN_INFO "No PLL1 clock\n");
+ enfc_clk = clk_get(NULL, "enfc_clk");
+ if (IS_ERR(enfc_clk)) {
+ printk(KERN_INFO "No enfc_clk clock\n");
return -EINVAL;
}
/* toggle nand : 133/66 MHz */
rate = 33000000;
- clk_set_parent(resources->clock, pll1);
+ clk_set_parent(resources->clock, enfc_clk);
+ clk_set_rate(enfc_clk, \
+ enfc_clk->round_rate(enfc_clk, rate));
clk_set_rate(resources->clock, rate);
}
nand->select_chip(mtd, 0);
diff --git a/drivers/mxc/gpu-viv/arch/GC350/hal/kernel/gc_hal_kernel_hardware_vg.c b/drivers/mxc/gpu-viv/arch/GC350/hal/kernel/gc_hal_kernel_hardware_vg.c
index 25a893cd614c..c24439d1af1e 100644
--- a/drivers/mxc/gpu-viv/arch/GC350/hal/kernel/gc_hal_kernel_hardware_vg.c
+++ b/drivers/mxc/gpu-viv/arch/GC350/hal/kernel/gc_hal_kernel_hardware_vg.c
@@ -1476,7 +1476,7 @@ static gceSTATUS _CommandStall(
gcmkERR_BREAK(gckOS_WaitSignal(
command->os,
command->powerStallSignal,
- gcvINFINITE));
+ gcdGPU_TIMEOUT));
}
@@ -1750,7 +1750,7 @@ gckVGHARDWARE_SetPowerManagementState(
if (flag & (gcvPOWER_FLAG_INITIALIZE | gcvPOWER_FLAG_CLOCK_ON))
{
/* Turn on the power. */
- gcmkONERROR(gckOS_SetGPUPower(os, gcvCORE_VG , gcvTRUE, gcvTRUE));
+ gcmkONERROR(gckOS_SetGPUPower(os, gcvCORE_VG, gcvTRUE, gcvTRUE));
/* Mark clock and power as enabled. */
Hardware->clockState = gcvTRUE;
diff --git a/drivers/mxc/gpu-viv/arch/XAQ2/hal/kernel/gc_hal_kernel_context.c b/drivers/mxc/gpu-viv/arch/XAQ2/hal/kernel/gc_hal_kernel_context.c
index 6ddbcee2bda4..a186bbe3386e 100644
--- a/drivers/mxc/gpu-viv/arch/XAQ2/hal/kernel/gc_hal_kernel_context.c
+++ b/drivers/mxc/gpu-viv/arch/XAQ2/hal/kernel/gc_hal_kernel_context.c
@@ -545,7 +545,8 @@ _InitializeContextBuffer(
index += _State(Context, index, 0x00A38 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
index += _State(Context, index, 0x00A3C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
index += _State(Context, index, 0x00A80 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
- index += _State(Context, index, 0x00A84 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00A84 >> 2, 0x00000000, 1, gcvTRUE, gcvFALSE);
+ index += _State(Context, index, 0x00A8C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
/* Setup states. */
index += _State(Context, index, 0x00C00 >> 2, 0x00000000, 1, gcvTRUE, gcvFALSE);
diff --git a/drivers/mxc/gpu-viv/arch/XAQ2/hal/kernel/gc_hal_kernel_hardware.c b/drivers/mxc/gpu-viv/arch/XAQ2/hal/kernel/gc_hal_kernel_hardware.c
index 99b699d3a6c8..1578ff2b09c5 100644
--- a/drivers/mxc/gpu-viv/arch/XAQ2/hal/kernel/gc_hal_kernel_hardware.c
+++ b/drivers/mxc/gpu-viv/arch/XAQ2/hal/kernel/gc_hal_kernel_hardware.c
@@ -217,16 +217,23 @@ _IdentifyHardware(
}
}
+ /* If new HZ is available, disable other early z modes. */
+ if (((((gctUINT32) (Identity->chipMinorFeatures3)) >> (0 ? 26:26) & ((gctUINT32) ((((1 ? 26:26) - (0 ? 26:26) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 26:26) - (0 ? 26:26) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 26:26) - (0 ? 26:26) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 26:26) - (0 ? 26:26) + 1)))))))
+ || ((((gctUINT32) (Identity->chipMinorFeatures3)) >> (0 ? 8:8) & ((gctUINT32) ((((1 ? 8:8) - (0 ? 8:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:8) - (0 ? 8:8) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 8:8) - (0 ? 8:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:8) - (0 ? 8:8) + 1))))))))
+ {
+ /* Disable EZ. */
+ Identity->chipFeatures
+ = ((((gctUINT32) (Identity->chipFeatures)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1))))))) << (0 ? 16:16))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1))))))) << (0 ? 16:16)));
+ }
+
/* Disable HZ when EZ is present for older chips. */
- if (!((((gctUINT32) (Identity->chipMinorFeatures3)) >> (0 ? 8:8) & ((gctUINT32) ((((1 ? 8:8) - (0 ? 8:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:8) - (0 ? 8:8) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 8:8) - (0 ? 8:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:8) - (0 ? 8:8) + 1)))))))
- &&
- !((((gctUINT32) (Identity->chipFeatures)) >> (0 ? 16:16) & ((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1)))))))
- )
+ else if (!((((gctUINT32) (Identity->chipFeatures)) >> (0 ? 16:16) & ((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1))))))))
{
/* Disable HIERARCHICAL_Z. */
Identity->chipMinorFeatures
= ((((gctUINT32) (Identity->chipMinorFeatures)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 27:27) - (0 ? 27:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 27:27) - (0 ? 27:27) + 1))))))) << (0 ? 27:27))) | (((gctUINT32) (0x0 & ((gctUINT32) ((((1 ? 27:27) - (0 ? 27:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 27:27) - (0 ? 27:27) + 1))))))) << (0 ? 27:27)));
}
+
/* Disable rectangle primitive when chip is gc880_5_1_0_rc6*/
if ((Identity->chipModel == gcv880) && (Identity->chipRevision == 0x5106))
{
@@ -234,6 +241,7 @@ _IdentifyHardware(
Identity->chipMinorFeatures2
= ((((gctUINT32) (Identity->chipMinorFeatures2)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5))) | (((gctUINT32) (0x0 & ((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5)));
}
+
gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
"Identity: chipFeatures=0x%08X",
Identity->chipFeatures);
@@ -494,7 +502,10 @@ gckHARDWARE_Construct(
/* Enable the GPU. */
gcmkONERROR(gckOS_SetGPUPower(Os, Core, gcvTRUE, gcvTRUE));
- gcmkONERROR(gckOS_WriteRegisterEx(Os, Core, 0x00000, 0));
+ gcmkONERROR(gckOS_WriteRegisterEx(Os,
+ Core,
+ 0x00000,
+ 0x00000900));
/* Allocate the gckHARDWARE object. */
gcmkONERROR(gckOS_Allocate(Os,
@@ -609,7 +620,7 @@ OnError:
if (hardware != gcvNULL)
{
/* Turn off the power. */
- gcmkVERIFY_OK(gckOS_SetGPUPower(Os, hardware->core, gcvFALSE, gcvFALSE));
+ gcmkVERIFY_OK(gckOS_SetGPUPower(Os, Core, gcvFALSE, gcvFALSE));
if (hardware->globalSemaphore != gcvNULL)
{
diff --git a/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel.c b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel.c
index 187b9cc6d810..773a262562c4 100644
--- a/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel.c
+++ b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel.c
@@ -289,7 +289,10 @@ OnError:
if (kernel->hardware != gcvNULL)
{
/* Turn off the power. */
- gcmkVERIFY_OK(gckOS_SetGPUPower(kernel->hardware->os, kernel->hardware->core, gcvFALSE, gcvFALSE));
+ gcmkVERIFY_OK(gckOS_SetGPUPower(kernel->hardware->os,
+ kernel->hardware->core,
+ gcvFALSE,
+ gcvFALSE));
gcmkVERIFY_OK(gckHARDWARE_Destroy(kernel->hardware));
}
}
@@ -1037,7 +1040,7 @@ gckKERNEL_Dispatch(
break;
case gcvHAL_UNMAP_USER_MEMORY:
- address = Interface->u.MapUserMemory.address;
+ address = Interface->u.UnmapUserMemory.address;
/* Unmap user memory. */
gcmkONERROR(
@@ -1169,9 +1172,10 @@ gckKERNEL_Dispatch(
#if gcdREGISTER_ACCESS_FROM_USER
{
gceCHIPPOWERSTATE power;
+
+ gckOS_AcquireMutex(Kernel->os, Kernel->hardware->powerMutex, gcvINFINITE);
gcmkONERROR(gckHARDWARE_QueryPowerManagementState(Kernel->hardware,
&power));
-
if (power == gcvPOWER_ON)
{
/* Read a register. */
@@ -1187,6 +1191,7 @@ gckKERNEL_Dispatch(
Interface->u.ReadRegisterData.data = 0;
status = gcvSTATUS_CHIP_NOT_READY;
}
+ gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->hardware->powerMutex));
}
#else
/* No access from user land to read registers. */
@@ -1197,12 +1202,29 @@ gckKERNEL_Dispatch(
case gcvHAL_WRITE_REGISTER:
#if gcdREGISTER_ACCESS_FROM_USER
- /* Write a register. */
- gcmkONERROR(
- gckOS_WriteRegisterEx(Kernel->os,
- Kernel->core,
- Interface->u.WriteRegisterData.address,
- Interface->u.WriteRegisterData.data));
+ {
+ gceCHIPPOWERSTATE power;
+
+ gckOS_AcquireMutex(Kernel->os, Kernel->hardware->powerMutex, gcvINFINITE);
+ gcmkONERROR(gckHARDWARE_QueryPowerManagementState(Kernel->hardware,
+ &power));
+ if (power == gcvPOWER_ON)
+ {
+ /* Write a register. */
+ gcmkONERROR(
+ gckOS_WriteRegisterEx(Kernel->os,
+ Kernel->core,
+ Interface->u.WriteRegisterData.address,
+ Interface->u.WriteRegisterData.data));
+ }
+ else
+ {
+ /* Chip is in power-state. */
+ Interface->u.WriteRegisterData.data = 0;
+ status = gcvSTATUS_CHIP_NOT_READY;
+ }
+ gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->hardware->powerMutex));
+ }
#else
/* No access from user land to write registers. */
status = gcvSTATUS_NOT_SUPPORTED;
diff --git a/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel.h b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel.h
index bdc94bc088b3..a9697a12c2e2 100644
--- a/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel.h
+++ b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel.h
@@ -613,24 +613,24 @@ typedef union _gcuVIDMEM_NODE
gctSIZE_T pageCount;
/* Used only when node is not contiguous */
- gctPOINTER pageTables[gcdCORE_COUNT];
+ gctPOINTER pageTables[gcdGPU_COUNT];
/* Pointer to gckKERNEL object who lock this. */
- gckKERNEL lockKernels[gcdCORE_COUNT];
+ gckKERNEL lockKernels[gcdGPU_COUNT];
/* Actual physical address */
- gctUINT32 addresses[gcdCORE_COUNT];
+ gctUINT32 addresses[gcdGPU_COUNT];
/* Mutex. */
gctPOINTER mutex;
/* Locked counter. */
- gctINT32 lockeds[gcdCORE_COUNT];
+ gctINT32 lockeds[gcdGPU_COUNT];
#ifdef __QNXNTO__
/* Single linked list of nodes. */
gcuVIDMEM_NODE_PTR next;
/* Unlock pending flag. */
- gctBOOL unlockPendings[gcdCORE_COUNT];
+ gctBOOL unlockPendings[gcdGPU_COUNT];
/* Free pending flag. */
gctBOOL freePending;
diff --git a/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_command.c b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_command.c
index 14ac175a1b2d..7a5e82eef3b4 100644
--- a/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_command.c
+++ b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_command.c
@@ -2480,8 +2480,6 @@ gckCOMMAND_Stall(
gcmkONERROR(gckOS_Broadcast(
os, hardware, gcvBROADCAST_GPU_STUCK
));
-
- gcmkONERROR(gcvSTATUS_GPU_NOT_RESPONDING);
}
/* Delete the signal. */
diff --git a/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_command_vg.c b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_command_vg.c
index 3a29f023ff80..227ab683778e 100644
--- a/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_command_vg.c
+++ b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_command_vg.c
@@ -1236,12 +1236,6 @@ _TaskUnmapUserMemory(
gcsBLOCK_TASK_ENTRY_PTR TaskHeader
);
-static gceSTATUS
-_TaskUnmapMemory(
- gckVGCOMMAND Command,
- gcsBLOCK_TASK_ENTRY_PTR TaskHeader
- );
-
static gctTASKROUTINE _taskRoutine[] =
{
_TaskLink, /* gcvTASK_LINK */
@@ -1254,7 +1248,6 @@ static gctTASKROUTINE _taskRoutine[] =
_TaskFreeVideoMemory, /* gcvTASK_FREE_VIDEO_MEMORY */
_TaskFreeContiguousMemory, /* gcvTASK_FREE_CONTIGUOUS_MEMORY */
_TaskUnmapUserMemory, /* gcvTASK_UNMAP_USER_MEMORY */
- _TaskUnmapMemory, /* gcvTASK_UNMAP_MEMORY */
};
static gceSTATUS
@@ -1676,38 +1669,6 @@ _TaskUnmapUserMemory(
return status;
}
-static gceSTATUS
-_TaskUnmapMemory(
- gckVGCOMMAND Command,
- gcsBLOCK_TASK_ENTRY_PTR TaskHeader
- )
-{
- gceSTATUS status;
-
- do
- {
- /* Cast the task pointer. */
- gcsTASK_UNMAP_MEMORY_PTR task
- = (gcsTASK_UNMAP_MEMORY_PTR) TaskHeader->task;
-
- /* Unmap memory. */
- gcmkERR_BREAK(gckKERNEL_UnmapMemory(
- Command->kernel->kernel, task->physical, task->bytes, task->logical
- ));
-
- /* Update the reference counter. */
- TaskHeader->container->referenceCount -= 1;
-
- /* Update the task pointer. */
- TaskHeader->task = (gcsTASK_HEADER_PTR) (task + 1);
- }
- while (gcvFALSE);
-
- /* Return status. */
- return status;
-}
-
-
/******************************************************************************\
************ Hardware Block Interrupt Handlers For Scheduled Events ************
\******************************************************************************/
diff --git a/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_debug.c b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_debug.c
index 77ea27a6c31e..00decd9fb09d 100644
--- a/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_debug.c
+++ b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_debug.c
@@ -1596,7 +1596,7 @@ _GetOutputBuffer(
#if gcdTHREAD_BUFFERS > 1
/* Get the current thread ID. */
- gctUINT32 threadID = gcmkGETTHREADID();
+ gctUINT32 ThreadID = gcmkGETTHREADID();
/* Locate the output buffer for the thread. */
outputBuffer = _outputBufferHead;
@@ -1629,10 +1629,14 @@ _GetOutputBuffer(
/* Reset the buffer. */
outputBuffer->threadID = ThreadID;
+#if gcdBUFFERED_OUTPUT
outputBuffer->start = 0;
outputBuffer->index = 0;
outputBuffer->count = 0;
+#endif
+#if gcdSHOW_LINE_NUMBER
outputBuffer->lineNumber = 0;
+#endif
}
#else
outputBuffer = _outputBufferHead;
diff --git a/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_event.c b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_event.c
index 4f8628c19946..43363b9e685b 100644
--- a/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_event.c
+++ b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_event.c
@@ -203,20 +203,54 @@ _TryToIdleGPU(
{
gceSTATUS status;
gctBOOL empty = gcvFALSE, idle = gcvFALSE;
+ gctUINT32 process, thread;
+ gctBOOL powerLocked = gcvFALSE;
+ gckHARDWARE hardware;
+
gcmkHEADER_ARG("Event=0x%x", Event);
/* Verify the arguments. */
gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+ /* Grab gckHARDWARE object. */
+ hardware = Event->kernel->hardware;
+ gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE);
+
+
/* Check whether the event queue is empty. */
gcmkONERROR(gckEVENT_IsEmpty(Event, &empty));
if (empty)
{
+ status = gckOS_AcquireMutex(hardware->os, hardware->powerMutex, 0);
+ if (status == gcvSTATUS_TIMEOUT)
+ {
+ gcmkONERROR(gckOS_GetProcessID(&process));
+ gcmkONERROR(gckOS_GetThreadID(&thread));
+
+ /* Just return to prevent deadlock. */
+ if ((hardware->powerProcess != process)
+ || (hardware->powerThread != thread))
+ {
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+ }
+ }
+ else
+ {
+ powerLocked = gcvTRUE;
+ }
+
/* Query whether the hardware is idle. */
gcmkONERROR(gckHARDWARE_QueryIdle(Event->kernel->hardware, &idle));
+ if (powerLocked)
+ {
+ gcmkONERROR(gckOS_ReleaseMutex(hardware->os, hardware->powerMutex));
+ powerLocked = gcvFALSE;
+ }
+
if (idle)
{
/* Inform the system of idle GPU. */
@@ -230,6 +264,13 @@ _TryToIdleGPU(
return gcvSTATUS_OK;
OnError:
+
+ if (powerLocked)
+ {
+ gcmkONERROR(gckOS_ReleaseMutex(hardware->os, hardware->powerMutex));
+ powerLocked = gcvFALSE;
+ }
+
gcmkFOOTER();
return status;
}
diff --git a/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_mmu.c b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_mmu.c
index deb658d98568..7062dd9a1b27 100644
--- a/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_mmu.c
+++ b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_mmu.c
@@ -68,7 +68,7 @@ typedef struct _gcsSharedPageTable
gckMMU mmu;
/* Hardwares which use this shared pagetable. */
- gckHARDWARE hardwares[gcdCORE_COUNT];
+ gckHARDWARE hardwares[gcdGPU_COUNT];
/* Number of cores use this shared pagetable. */
gctUINT32 reference;
@@ -1201,7 +1201,7 @@ gckMMU_Enable(
gcmkONERROR(_SetupDynamicSpace(Mmu));
#if gcdSHARED_PAGETABLE
- for(i = 0; i < gcdCORE_COUNT; i++)
+ for(i = 0; i < gcdGPU_COUNT; i++)
{
hardware = sharedPageTable->hardwares[i];
if (hardware != gcvNULL)
@@ -1402,7 +1402,7 @@ gckMMU_Flush(
gckHARDWARE hardware;
#if gcdSHARED_PAGETABLE
gctINT i;
- for (i = 0; i < gcdCORE_COUNT; i++)
+ for (i = 0; i < gcdGPU_COUNT; i++)
{
#if gcdENABLE_VG
if (i == gcvCORE_VG)
diff --git a/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_video_memory.c b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_video_memory.c
index 88e4d7705bd8..89a86a8486a7 100644
--- a/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_video_memory.c
+++ b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_video_memory.c
@@ -245,7 +245,7 @@ gckVIDMEM_ConstructVirtual(
node->Virtual.contiguous = Contiguous;
node->Virtual.logical = gcvNULL;
- for (i = 0; i < gcdCORE_COUNT; i++)
+ for (i = 0; i < gcdGPU_COUNT; i++)
{
node->Virtual.lockeds[i] = 0;
node->Virtual.pageTables[i] = gcvNULL;
@@ -259,7 +259,7 @@ gckVIDMEM_ConstructVirtual(
#ifdef __QNXNTO__
node->Virtual.next = gcvNULL;
node->Virtual.freePending = gcvFALSE;
- for (i = 0; i < gcdCORE_COUNT; i++)
+ for (i = 0; i < gcdGPU_COUNT; i++)
{
node->Virtual.unlockPendings[i] = gcvFALSE;
}
@@ -366,7 +366,7 @@ gckVIDMEM_DestroyVirtual(
/* Delete the mutex. */
gcmkVERIFY_OK(gckOS_DeleteMutex(os, Node->Virtual.mutex));
- for (i = 0; i < gcdCORE_COUNT; i++)
+ for (i = 0; i < gcdGPU_COUNT; i++)
{
if (Node->Virtual.pageTables[i] != gcvNULL)
{
@@ -1392,7 +1392,7 @@ gckVIDMEM_Free(
acquired = gcvTRUE;
- for (i = 0, totalLocked = 0; i < gcdCORE_COUNT; i++)
+ for (i = 0, totalLocked = 0; i < gcdGPU_COUNT; i++)
{
totalLocked += Node->Virtual.lockeds[i];
}
@@ -1590,7 +1590,7 @@ _NeedVirtualMapping(
gcmkVERIFY_ARGUMENT(Kernel != gcvNULL);
gcmkVERIFY_ARGUMENT(Node != gcvNULL);
gcmkVERIFY_ARGUMENT(NeedMapping != gcvNULL);
- gcmkVERIFY_ARGUMENT(Core < gcdCORE_COUNT);
+ gcmkVERIFY_ARGUMENT(Core < gcdGPU_COUNT);
if (Node->Virtual.contiguous)
{
@@ -2052,7 +2052,7 @@ gckVIDMEM_Unlock(
#endif
}
- for (i = 0, totalLocked = 0; i < gcdCORE_COUNT; i++)
+ for (i = 0, totalLocked = 0; i < gcdGPU_COUNT; i++)
{
totalLocked += Node->Virtual.lockeds[i];
}
diff --git a/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal.h b/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal.h
index 87b811d8dfaf..f986e4e4e009 100644
--- a/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal.h
+++ b/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal.h
@@ -137,7 +137,7 @@ typedef enum _gceCORE
}
gceCORE;
-#define gcdCORE_COUNT 3
+#define gcdGPU_COUNT 3
/*******************************************************************************
**
@@ -1226,8 +1226,8 @@ gckOS_BroadcastCalibrateSpeed(
** gckOS Os
** Pointer to a gckOS object.ß
**
-** gceCORE Core
-** Core type.
+** gckCORE Core
+** GPU whose power is set.
**
** gctBOOL Clock
** gcvTRUE to turn on the clock, or gcvFALSE to turn off the clock.
diff --git a/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_base.h b/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_base.h
index 5fafefbe058e..b1e6b488f0af 100644
--- a/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_base.h
+++ b/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_base.h
@@ -129,6 +129,7 @@ typedef struct _gcsTLS
gco3D engine3D;
#endif
gco2D engine2D;
+ gctBOOL copied;
}
gcsTLS;
@@ -642,6 +643,9 @@ gcoOS_GetTLS(
OUT gcsTLS_PTR * TLS
);
+ /* Copy the TLS from a source thread. */
+ gceSTATUS gcoOS_CopyTLS(IN gcsTLS_PTR Source);
+
/* Destroy the objects associated with the current thread. */
void
gcoOS_FreeThreadData(
@@ -2061,6 +2065,11 @@ gcoDUMP_Delete(
IN gctUINT32 Address
);
+/* Enable dump or not. */
+gceSTATUS
+gcoDUMP_SetDumpFlag(
+ IN gctBOOL DumpState
+ );
/******************************************************************************\
******************************* gcsRECT Structure ******************************
diff --git a/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_driver_vg.h b/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_driver_vg.h
index fe57968ba423..447663f964bd 100644
--- a/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_driver_vg.h
+++ b/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_driver_vg.h
@@ -109,8 +109,7 @@ typedef enum _gceTASK
gcvTASK_UNLOCK_VIDEO_MEMORY,
gcvTASK_FREE_VIDEO_MEMORY,
gcvTASK_FREE_CONTIGUOUS_MEMORY,
- gcvTASK_UNMAP_USER_MEMORY,
- gcvTASK_UNMAP_MEMORY
+ gcvTASK_UNMAP_USER_MEMORY
}
gceTASK;
@@ -268,23 +267,6 @@ typedef struct _gcsTASK_UNMAP_USER_MEMORY
}
gcsTASK_UNMAP_USER_MEMORY;
-typedef struct _gcsTASK_UNMAP_MEMORY * gcsTASK_UNMAP_MEMORY_PTR;
-typedef struct _gcsTASK_UNMAP_MEMORY
-{
- /* Task ID (gcvTASK_UNMAP_MEMORY). */
- IN gceTASK id;
-
- /* Physical memory address to unmap. */
- IN gctPHYS_ADDR physical;
-
- /* Number of bytes in physical memory to unmap. */
- IN gctSIZE_T bytes;
-
- /* Address of mapped memory to unmap. */
- IN gctPOINTER logical;
-}
-gcsTASK_UNMAP_MEMORY;
-
#ifdef __cplusplus
}
#endif
diff --git a/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_eglplatform.h b/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_eglplatform.h
index f4b7d2886e8e..b9bf7c8f916e 100644
--- a/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_eglplatform.h
+++ b/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_eglplatform.h
@@ -251,11 +251,63 @@ typedef struct __BITFIELDINFO{
} BITFIELDINFO;
#elif defined(LINUX) && defined(EGL_API_FB) && !defined(__APPLE__)
+
+#if defined(EGL_API_WL)
+/* Wayland platform. */
+
+#include <wayland-egl.h>
+
+#define WL_EGL_NUM_BACKBUFFERS 2
+
+struct wl_egl_buffer_info
+{
+ gctINT32 width;
+ gctINT32 height;
+ gctINT32 stride;
+ gctUINT32 physical;
+ gctPOINTER logical;
+ gceSURF_FORMAT format;
+ gcoSURF surface;
+};
+
+struct wl_egl_buffer
+{
+ struct wl_buffer* wl_buffer;
+ struct wl_egl_buffer_info info;
+};
+
+struct wl_egl_window_info
+{
+ gctUINT width;
+ gctUINT height;
+ gceSURF_FORMAT format;
+ gctUINT bpp;
+};
+
+struct wl_egl_window
+{
+/* struct wl_egl_display *display;*/
+ struct wl_surface* surface;
+ struct wl_egl_window_info info;
+ struct wl_egl_buffer backbuffers[WL_EGL_NUM_BACKBUFFERS];
+ gctUINT current;
+ /*
+ int backbuffer;
+ int dx;
+ int dy;
+*/
+};
+
+
+typedef void* HALNativeDisplayType;
+typedef void* HALNativeWindowType;
+typedef void* HALNativePixmapType;
+#else
/* Linux platform for FBDEV. */
typedef struct _FBDisplay * HALNativeDisplayType;
typedef struct _FBWindow * HALNativeWindowType;
typedef struct _FBPixmap * HALNativePixmapType;
-
+#endif
#elif defined(__ANDROID__) || defined(ANDROID)
struct egl_native_pixmap_t;
@@ -370,6 +422,8 @@ typedef struct _halDISPLAY_INFO
** if the address is not known for the specified display. */
gctSIZE_T physical;
+ gctBOOL isCompositor; /* true if compositor, false otherwise. */
+
#ifndef __QNXNTO__
/* 355_FB_MULTI_BUFFER */
gctINT multiBuffer;
diff --git a/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_engine.h b/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_engine.h
index f77cf5826a6a..7ad46da0b40e 100644
--- a/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_engine.h
+++ b/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_engine.h
@@ -1173,6 +1173,27 @@ gco3D_InvokeThreadWalker(
IN gcsTHREAD_WALKER_INFO_PTR Info
);
+#if gcdUSE_WCLIP_PATCH
+/* Set w clip and w plane limit value. */
+gceSTATUS
+gco3D_SetWClipEnable(
+ IN gco3D Engine,
+ IN gctBOOL Enable
+ );
+
+gceSTATUS
+gco3D_SetWPlaneLimitF(
+ IN gco3D Engine,
+ IN gctFLOAT Value
+ );
+
+gceSTATUS
+gco3D_SetWPlaneLimitX(
+ IN gco3D Engine,
+ IN gctFIXED_POINT Value
+ );
+#endif
+
/*----------------------------------------------------------------------------*/
/*-------------------------- gco3D Fragment Processor ------------------------*/
@@ -1437,6 +1458,7 @@ gceSTATUS
gcoTEXTURE_AddMipMap(
IN gcoTEXTURE Texture,
IN gctINT Level,
+ IN gctINT imageFormat,
IN gceSURF_FORMAT Format,
IN gctUINT Width,
IN gctUINT Height,
diff --git a/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_enum.h b/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_enum.h
index d10686a259c3..18ddf5bff754 100644
--- a/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_enum.h
+++ b/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_enum.h
@@ -144,6 +144,7 @@ typedef enum _gceFEATURE
gcvFEATURE_RS_YUV_TARGET,
gcvFEATURE_2D_FC_SOURCE,
gcvFEATURE_PE_DITHER_FIX,
+ gcvFEATURE_2D_YUV_SEPARATE_STRIDE,
}
gceFEATURE;
@@ -263,6 +264,13 @@ typedef enum _gceSURF_ROTATION
}
gceSURF_ROTATION;
+typedef enum _gceMIPMAP_IMAGE_FORMAT
+{
+ gcvUNKNOWN_MIPMAP_IMAGE_FORMAT = -2
+}
+gceMIPMAP_IMAGE_FORMAT;
+
+
/* Surface formats. */
typedef enum _gceSURF_FORMAT
{
diff --git a/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_options.h b/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_options.h
index 73b666a37570..35743c62f2a2 100644
--- a/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_options.h
+++ b/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_options.h
@@ -258,7 +258,7 @@
virtual data.
*/
#ifndef gcdMMU_SIZE
-# define gcdMMU_SIZE (128 << 10)
+# define gcdMMU_SIZE (2048 << 10)
#endif
/*
@@ -778,4 +778,8 @@
# define gcdHIGH_PRECISION_DELAY_ENABLE 1
#endif
+#ifndef gcdUSE_WCLIP_PATCH
+# define gcdUSE_WCLIP_PATCH 0
+#endif
+
#endif /* __gc_hal_options_h_ */
diff --git a/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_version.h b/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_version.h
index 132832c9224d..0bc1ffc46943 100644
--- a/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_version.h
+++ b/drivers/mxc/gpu-viv/hal/kernel/inc/gc_hal_version.h
@@ -28,9 +28,9 @@
#define gcvVERSION_MINOR 6
-#define gcvVERSION_PATCH 7
+#define gcvVERSION_PATCH 8
-#define gcvVERSION_BUILD 1422
+#define gcvVERSION_BUILD 1443
#define gcvVERSION_DATE __DATE__
diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c
index df75a8b3c65c..305b1cc9acb6 100644
--- a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c
+++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c
@@ -404,7 +404,7 @@ gckGALDEVICE_Construct(
device->requestedContiguousSize = 0;
- for (i = 0; i < gcdCORE_COUNT; i++)
+ for (i = 0; i < gcdGPU_COUNT; i++)
{
physical = device->requestedRegisterMemBases[i];
@@ -582,19 +582,19 @@ gckGALDEVICE_Construct(
device->irqLines[gcvCORE_VG] = IrqLineVG;
/* Initialize the kernel thread semaphores. */
- for (i = 0; i < gcdCORE_COUNT; i++)
+ for (i = 0; i < gcdGPU_COUNT; i++)
{
if (device->irqLines[i] != -1) sema_init(&device->semas[i], 0);
}
device->signal = Signal;
- for (i = 0; i < gcdCORE_COUNT; i++)
+ for (i = 0; i < gcdGPU_COUNT; i++)
{
if (device->kernels[i] != gcvNULL) break;
}
- if (i == gcdCORE_COUNT) gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ if (i == gcdGPU_COUNT) gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
#if gcdENABLE_VG
if (i == gcvCORE_VG)
@@ -854,7 +854,7 @@ gckGALDEVICE_Destroy(
if (Device != gcvNULL)
{
- for (i = 0; i < gcdCORE_COUNT; i++)
+ for (i = 0; i < gcdGPU_COUNT; i++)
{
if (Device->kernels[i] != gcvNULL)
{
@@ -937,7 +937,7 @@ gckGALDEVICE_Destroy(
}
}
- for (i = 0; i < gcdCORE_COUNT; i++)
+ for (i = 0; i < gcdGPU_COUNT; i++)
{
if (Device->registerBases[i] != gcvNULL)
{
@@ -1428,7 +1428,7 @@ gckGALDEVICE_Stop_Threads(
gcmkVERIFY_ARGUMENT(Device != NULL);
- for (i = 0; i < gcdCORE_COUNT; i++)
+ for (i = 0; i < gcdGPU_COUNT; i++)
{
/* Stop the kernel threads. */
if (Device->threadInitializeds[i])
diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.h b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.h
index 69c985c90ff3..9e4f5b48e78f 100644
--- a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.h
+++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.h
@@ -38,7 +38,7 @@ typedef struct _gckGALDEVICE
{
/* Objects. */
gckOS os;
- gckKERNEL kernels[gcdCORE_COUNT];
+ gckKERNEL kernels[gcdGPU_COUNT];
/* Attributes. */
gctSIZE_T internalSize;
@@ -57,23 +57,23 @@ typedef struct _gckGALDEVICE
gctPOINTER contiguousMappedUser;
gctSIZE_T systemMemorySize;
gctUINT32 systemMemoryBaseAddress;
- gctPOINTER registerBases[gcdCORE_COUNT];
- gctSIZE_T registerSizes[gcdCORE_COUNT];
+ gctPOINTER registerBases[gcdGPU_COUNT];
+ gctSIZE_T registerSizes[gcdGPU_COUNT];
gctUINT32 baseAddress;
- gctUINT32 requestedRegisterMemBases[gcdCORE_COUNT];
- gctSIZE_T requestedRegisterMemSizes[gcdCORE_COUNT];
+ gctUINT32 requestedRegisterMemBases[gcdGPU_COUNT];
+ gctSIZE_T requestedRegisterMemSizes[gcdGPU_COUNT];
gctUINT32 requestedContiguousBase;
gctSIZE_T requestedContiguousSize;
/* IRQ management. */
- gctINT irqLines[gcdCORE_COUNT];
- gctBOOL isrInitializeds[gcdCORE_COUNT];
- gctBOOL dataReadys[gcdCORE_COUNT];
+ gctINT irqLines[gcdGPU_COUNT];
+ gctBOOL isrInitializeds[gcdGPU_COUNT];
+ gctBOOL dataReadys[gcdGPU_COUNT];
/* Thread management. */
- struct task_struct *threadCtxts[gcdCORE_COUNT];
- struct semaphore semas[gcdCORE_COUNT];
- gctBOOL threadInitializeds[gcdCORE_COUNT];
+ struct task_struct *threadCtxts[gcdGPU_COUNT];
+ struct semaphore semas[gcdGPU_COUNT];
+ gctBOOL threadInitializeds[gcdGPU_COUNT];
gctBOOL killThread;
/* Signal management. */
@@ -83,7 +83,7 @@ typedef struct _gckGALDEVICE
gceCORE coreMapping[8];
/* States before suspend. */
- gceCHIPPOWERSTATE statesStored[gcdCORE_COUNT];
+ gceCHIPPOWERSTATE statesStored[gcdGPU_COUNT];
/* Clock management.*/
struct clk *clk_3d_core;
diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_driver.c b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_driver.c
index 0daf9a1cb149..c5ce248e77ba 100644
--- a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_driver.c
+++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_driver.c
@@ -85,7 +85,7 @@ module_param(contiguousSize, ulong, 0644);
static ulong contiguousBase = 0;
module_param(contiguousBase, ulong, 0644);
-static ulong bankSize = 32 << 20;
+static ulong bankSize = 0;
module_param(bankSize, ulong, 0644);
static int fastClear = -1;
@@ -182,7 +182,7 @@ int drv_open(
gcmkONERROR(gckOS_GetProcessID(&data->pidOpen));
/* Attached the process. */
- for (i = 0; i < gcdCORE_COUNT; i++)
+ for (i = 0; i < gcdGPU_COUNT; i++)
{
if (galDevice->kernels[i] != gcvNULL)
{
@@ -225,7 +225,7 @@ OnError:
if (attached)
{
- for (i = 0; i < gcdCORE_COUNT; i++)
+ for (i = 0; i < gcdGPU_COUNT; i++)
{
if (galDevice->kernels[i] != gcvNULL)
{
@@ -304,7 +304,7 @@ int drv_release(
}
/* A process gets detached. */
- for (i = 0; i < gcdCORE_COUNT; i++)
+ for (i = 0; i < gcdGPU_COUNT; i++)
{
if (galDevice->kernels[i] != gcvNULL)
{
@@ -442,7 +442,7 @@ long drv_ioctl(
if (iface.command == gcvHAL_CHIP_INFO)
{
count = 0;
- for (i = 0; i < gcdCORE_COUNT; i++)
+ for (i = 0; i < gcdGPU_COUNT; i++)
{
if (device->kernels[i] != gcvNULL)
{
@@ -959,7 +959,7 @@ static int __devinit gpu_suspend(struct platform_device *dev, pm_message_t state
device = platform_get_drvdata(dev);
- for (i = 0; i < gcdCORE_COUNT; i++)
+ for (i = 0; i < gcdGPU_COUNT; i++)
{
if (device->kernels[i] != gcvNULL)
{
@@ -990,8 +990,6 @@ static int __devinit gpu_suspend(struct platform_device *dev, pm_message_t state
{
status = gckHARDWARE_SetPowerManagementState(device->kernels[i]->hardware, gcvPOWER_OFF);
}
- /*gpu clock must be turned on before power down*/
- gckOS_SetGPUPower(device->os, i, gcvTRUE, gcvFALSE);
if (gcmIS_ERROR(status))
{
return -1;
@@ -1012,7 +1010,7 @@ static int __devinit gpu_resume(struct platform_device *dev)
device = platform_get_drvdata(dev);
- for (i = 0; i < gcdCORE_COUNT; i++)
+ for (i = 0; i < gcdGPU_COUNT; i++)
{
if (device->kernels[i] != gcvNULL)
{
diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c
index cf0ccc2d4e2b..63ac50912833 100644
--- a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c
+++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c
@@ -197,7 +197,6 @@ typedef struct _gcsSIGNAL
/* ID. */
gctUINT32 id;
-
}
gcsSIGNAL;
@@ -1150,6 +1149,52 @@ _DestroyIntegerId(
return gcvSTATUS_OK;
}
+static void
+_UnmapUserLogical(
+ IN gctINT Pid,
+ IN gctPOINTER Logical,
+ IN gctUINT32 Size
+)
+{
+ struct task_struct *task;
+ struct mm_struct *mm;
+
+ /* Get the task_struct of the task with stored pid. */
+ rcu_read_lock();
+
+ task = FIND_TASK_BY_PID(Pid);
+
+ if (task == gcvNULL)
+ {
+ rcu_read_unlock();
+ return;
+ }
+
+ /* Get the mm_struct. */
+ mm = get_task_mm(task);
+
+ rcu_read_unlock();
+
+ if (mm == gcvNULL)
+ {
+ return;
+ }
+
+ down_write(&mm->mmap_sem);
+ if (do_munmap(mm, (unsigned long)Logical, Size) < 0)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_WARNING, gcvZONE_OS,
+ "%s(%d): do_munmap failed",
+ __FUNCTION__, __LINE__
+ );
+ }
+ up_write(&mm->mmap_sem);
+
+ /* Dereference. */
+ mmput(mm);
+}
+
/*******************************************************************************
**
** gckOS_Construct
@@ -1329,7 +1374,6 @@ gckOS_Destroy(
*/
/* Destroy the mutex. */
-
gcmkVERIFY_OK(gckOS_DeleteMutex(Os, Os->signalMutex));
if (Os->heap != gcvNULL)
@@ -1898,7 +1942,6 @@ gckOS_UnmapMemoryEx(
{
PLINUX_MDL_MAP mdlMap;
PLINUX_MDL mdl = (PLINUX_MDL)Physical;
- struct task_struct * task;
gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%lu Logical=0x%X PID=%d",
Os, Physical, Bytes, Logical, PID);
@@ -1924,24 +1967,7 @@ gckOS_UnmapMemoryEx(
return gcvSTATUS_INVALID_ARGUMENT;
}
- /* Get the current pointer for the task with stored pid. */
- task = FIND_TASK_BY_PID(mdlMap->pid);
-
- if (task != gcvNULL && task->mm != gcvNULL)
- {
- down_write(&task->mm->mmap_sem);
- do_munmap(task->mm, (unsigned long)Logical, mdl->numPages*PAGE_SIZE);
- up_write(&task->mm->mmap_sem);
- }
- else
- {
- gcmkTRACE_ZONE(
- gcvLEVEL_INFO, gcvZONE_OS,
- "%s(%d): can't find the task with pid->%d. No unmapping",
- __FUNCTION__, __LINE__,
- mdlMap->pid
- );
- }
+ _UnmapUserLogical(PID, mdlMap->vmaAddr, mdl->numPages * PAGE_SIZE);
gcmkVERIFY_OK(_DestroyMdlMap(mdl, mdlMap));
}
@@ -2287,7 +2313,6 @@ gceSTATUS gckOS_FreeNonPagedMemory(
{
PLINUX_MDL mdl;
PLINUX_MDL_MAP mdlMap;
- struct task_struct * task;
#ifdef NO_DMA_COHERENT
unsigned size;
gctPOINTER vaddr;
@@ -2350,27 +2375,7 @@ gceSTATUS gckOS_FreeNonPagedMemory(
{
if (mdlMap->vmaAddr != gcvNULL)
{
- /* Get the current pointer for the task with stored pid. */
- task = FIND_TASK_BY_PID(mdlMap->pid);
-
- if (task != gcvNULL && task->mm != gcvNULL)
- {
- down_write(&task->mm->mmap_sem);
-
- if (do_munmap(task->mm,
- (unsigned long)mdlMap->vmaAddr,
- mdl->numPages * PAGE_SIZE) < 0)
- {
- gcmkTRACE_ZONE(
- gcvLEVEL_WARNING, gcvZONE_OS,
- "%s(%d): do_munmap failed",
- __FUNCTION__, __LINE__
- );
- }
-
- up_write(&task->mm->mmap_sem);
- }
-
+ _UnmapUserLogical(mdlMap->pid, mdlMap->vmaAddr, mdl->numPages * PAGE_SIZE);
mdlMap->vmaAddr = gcvNULL;
}
@@ -4687,7 +4692,6 @@ gckOS_UnlockPages(
{
PLINUX_MDL_MAP mdlMap;
PLINUX_MDL mdl = (PLINUX_MDL)Physical;
- struct task_struct * task;
gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%u Logical=0x%X",
Os, Physical, Bytes, Logical);
@@ -4709,16 +4713,7 @@ gckOS_UnlockPages(
{
if ((mdlMap->vmaAddr != gcvNULL) && (_GetProcessID() == mdlMap->pid))
{
- /* Get the current pointer for the task with stored pid. */
- task = FIND_TASK_BY_PID(mdlMap->pid);
-
- if (task != gcvNULL && task->mm != gcvNULL)
- {
- down_write(&task->mm->mmap_sem);
- do_munmap(task->mm, (unsigned long)mdlMap->vmaAddr, mdl->numPages * PAGE_SIZE);
- up_write(&task->mm->mmap_sem);
- }
-
+ _UnmapUserLogical(mdlMap->pid, mdlMap->vmaAddr, mdl->numPages * PAGE_SIZE);
mdlMap->vmaAddr = gcvNULL;
}
@@ -6914,8 +6909,8 @@ gckOS_GetThreadID(
** gckOS Os
** Pointer to a gckOS object.
**
-** gceCORE Core
-** Core type.
+** gckCORE Core
+** GPU whose power is set.
**
** gctBOOL Clock
** gcvTRUE to turn on the clock, or gcvFALSE to turn off the clock.
@@ -7597,7 +7592,6 @@ gckOS_MapSignal(
{
gceSTATUS status;
gcsSIGNAL_PTR signal;
-
gcmkHEADER_ARG("Os=0x%X Signal=0x%X Process=0x%X", Os, Signal, Process);
gcmkVERIFY_ARGUMENT(Signal != gcvNULL);
@@ -7605,7 +7599,7 @@ gckOS_MapSignal(
gcmkONERROR(_QueryIntegerId(&Os->signalDB, (gctUINT32)Signal, (gctPOINTER)&signal));
- if (atomic_inc_return(&signal->ref) <= 1)
+ if(atomic_inc_return(&signal->ref) <= 1)
{
/* The previous value is 0, it has been deleted. */
gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
@@ -7674,6 +7668,7 @@ gckOS_CreateUserSignal(
OUT gctINT * SignalID
)
{
+ /* Create a new signal. */
return gckOS_CreateSignal(Os, ManualReset, (gctSIGNAL *) SignalID);
}
diff --git a/drivers/mxc/ipu3/ipu_common.c b/drivers/mxc/ipu3/ipu_common.c
index 61687ad1411c..db22753e8276 100644
--- a/drivers/mxc/ipu3/ipu_common.c
+++ b/drivers/mxc/ipu3/ipu_common.c
@@ -40,6 +40,7 @@
#include "ipu_regs.h"
#include "ipu_param_mem.h"
+static DEFINE_MUTEX(ipu_clk_lock);
static struct ipu_soc ipu_array[MXC_IPU_MAX_NUM];
int g_ipu_hw_rev;
@@ -149,8 +150,8 @@ static int __devinit ipu_clk_setup_enable(struct ipu_soc *ipu,
}
dev_dbg(ipu->dev, "ipu_clk = %lu\n", clk_get_rate(ipu->ipu_clk));
- ipu->pixel_clk[0] = ipu_pixel_clk[0];
- ipu->pixel_clk[1] = ipu_pixel_clk[1];
+ ipu->pixel_clk[0] = ipu_pixel_clk[pdev->id][0];
+ ipu->pixel_clk[1] = ipu_pixel_clk[pdev->id][1];
ipu_lookups[pdev->id][0].clk = &ipu->pixel_clk[0];
ipu_lookups[pdev->id][1].clk = &ipu->pixel_clk[1];
@@ -371,14 +372,24 @@ void _ipu_unlock(struct ipu_soc *ipu)
void _ipu_get(struct ipu_soc *ipu)
{
- if (atomic_inc_return(&ipu->ipu_use_count) == 1)
- clk_enable(ipu->ipu_clk);
+ int ret;
+
+ if (in_interrupt())
+ return;
+ mutex_lock(&ipu_clk_lock);
+ ret = clk_enable(ipu->ipu_clk);
+ if (ret < 0)
+ BUG();
+ mutex_unlock(&ipu_clk_lock);
}
void _ipu_put(struct ipu_soc *ipu)
{
- if (atomic_dec_return(&ipu->ipu_use_count) == 0)
- clk_disable(ipu->ipu_clk);
+ if (in_interrupt())
+ return;
+ mutex_lock(&ipu_clk_lock);
+ clk_disable(ipu->ipu_clk);
+ mutex_unlock(&ipu_clk_lock);
}
/*!
@@ -406,7 +417,6 @@ static int __devinit ipu_probe(struct platform_device *pdev)
spin_lock_init(&ipu->spin_lock);
mutex_init(&ipu->mutex_lock);
- atomic_set(&ipu->ipu_use_count, 0);
g_ipu_hw_rev = plat_data->rev;
@@ -2683,10 +2693,13 @@ EXPORT_SYMBOL(ipu_clear_irq);
bool ipu_get_irq_status(struct ipu_soc *ipu, uint32_t irq)
{
uint32_t reg;
+ unsigned long lock_flags;
_ipu_get(ipu);
+ spin_lock_irqsave(&ipu->spin_lock, lock_flags);
reg = ipu_cm_read(ipu, IPUIRQ_2_STATREG(irq));
+ spin_unlock_irqrestore(&ipu->spin_lock, lock_flags);
_ipu_put(ipu);
@@ -2722,6 +2735,7 @@ int ipu_request_irq(struct ipu_soc *ipu, uint32_t irq,
irqreturn_t(*handler) (int, void *),
uint32_t irq_flags, const char *devname, void *dev_id)
{
+ uint32_t reg;
unsigned long lock_flags;
BUG_ON(irq >= IPU_IRQ_COUNT);
@@ -2744,13 +2758,15 @@ int ipu_request_irq(struct ipu_soc *ipu, uint32_t irq,
/* clear irq stat for previous use */
ipu_cm_write(ipu, IPUIRQ_2_MASK(irq), IPUIRQ_2_STATREG(irq));
+ /* enable the interrupt */
+ reg = ipu_cm_read(ipu, IPUIRQ_2_CTRLREG(irq));
+ reg |= IPUIRQ_2_MASK(irq);
+ ipu_cm_write(ipu, reg, IPUIRQ_2_CTRLREG(irq));
spin_unlock_irqrestore(&ipu->spin_lock, lock_flags);
_ipu_put(ipu);
- ipu_enable_irq(ipu, irq); /* enable the interrupt */
-
return 0;
}
EXPORT_SYMBOL(ipu_request_irq);
@@ -2769,14 +2785,23 @@ EXPORT_SYMBOL(ipu_request_irq);
*/
void ipu_free_irq(struct ipu_soc *ipu, uint32_t irq, void *dev_id)
{
+ uint32_t reg;
unsigned long lock_flags;
- ipu_disable_irq(ipu, irq); /* disable the interrupt */
+ _ipu_get(ipu);
spin_lock_irqsave(&ipu->spin_lock, lock_flags);
+
+ /* disable the interrupt */
+ reg = ipu_cm_read(ipu, IPUIRQ_2_CTRLREG(irq));
+ reg &= ~IPUIRQ_2_MASK(irq);
+ ipu_cm_write(ipu, reg, IPUIRQ_2_CTRLREG(irq));
if (ipu->irq_list[irq].dev_id == dev_id)
ipu->irq_list[irq].handler = NULL;
+
spin_unlock_irqrestore(&ipu->spin_lock, lock_flags);
+
+ _ipu_put(ipu);
}
EXPORT_SYMBOL(ipu_free_irq);
@@ -2855,11 +2880,11 @@ uint32_t ipu_channel_status(struct ipu_soc *ipu, ipu_channel_t channel)
{
uint32_t dma_status;
- _ipu_lock(ipu);
_ipu_get(ipu);
+ _ipu_lock(ipu);
dma_status = ipu_is_channel_busy(ipu, channel);
- _ipu_put(ipu);
_ipu_unlock(ipu);
+ _ipu_put(ipu);
dev_dbg(ipu->dev, "%s, dma_status:%d.\n", __func__, dma_status);
@@ -2979,13 +3004,16 @@ bool ipu_pixel_format_has_alpha(uint32_t fmt)
return false;
}
-static int ipu_suspend_noirq(struct device *dev)
+static int ipu_suspend(struct platform_device *pdev, pm_message_t state)
{
- struct platform_device *pdev = to_platform_device(dev);
struct imx_ipuv3_platform_data *plat_data = pdev->dev.platform_data;
struct ipu_soc *ipu = platform_get_drvdata(pdev);
+ int i;
- if (atomic_read(&ipu->ipu_use_count)) {
+ mutex_lock(&ipu_clk_lock);
+ ipu->ipu_use_count = clk_get_usecount(ipu->ipu_clk);
+ dev_dbg(ipu->dev, "%s, ipu_use_cnt:%d\n", __func__, ipu->ipu_use_count);
+ if (ipu->ipu_use_count) {
/* save and disable enabled channels*/
ipu->idma_enable_reg[0] = ipu_idmac_read(ipu, IDMAC_CHA_EN(0));
ipu->idma_enable_reg[1] = ipu_idmac_read(ipu, IDMAC_CHA_EN(32));
@@ -3009,7 +3037,7 @@ static int ipu_suspend_noirq(struct device *dev)
msleep(2);
time += 2;
if (time >= timeout)
- return -1;
+ goto err;
}
ipu_idmac_write(ipu, 0, IDMAC_CHA_EN(0));
ipu_idmac_write(ipu, 0, IDMAC_CHA_EN(32));
@@ -3051,26 +3079,34 @@ static int ipu_suspend_noirq(struct device *dev)
ipu->buf_ready_reg[8] = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(0));
ipu->buf_ready_reg[9] = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(32));
- clk_disable(ipu->ipu_clk);
+ for (i = 0; i < ipu->ipu_use_count; i++)
+ clk_disable(ipu->ipu_clk);
}
+ mutex_unlock(&ipu_clk_lock);
if (plat_data->pg)
plat_data->pg(1);
return 0;
+
+err:
+ mutex_unlock(&ipu_clk_lock);
+ return -1;
}
-static int ipu_resume_noirq(struct device *dev)
+static int ipu_resume(struct platform_device *pdev)
{
- struct platform_device *pdev = to_platform_device(dev);
struct imx_ipuv3_platform_data *plat_data = pdev->dev.platform_data;
struct ipu_soc *ipu = platform_get_drvdata(pdev);
+ int i;
if (plat_data->pg)
plat_data->pg(0);
- if (atomic_read(&ipu->ipu_use_count)) {
- clk_enable(ipu->ipu_clk);
+ dev_dbg(ipu->dev, "%s, ipu_use_cnt:%d\n", __func__, ipu->ipu_use_count);
+ if (ipu->ipu_use_count) {
+ for (i = 0; i < ipu->ipu_use_count; i++)
+ _ipu_get(ipu);
/* restore buf ready regs */
ipu_cm_write(ipu, ipu->buf_ready_reg[0], IPU_CHA_BUF0_RDY(0));
@@ -3122,20 +3158,16 @@ static int ipu_resume_noirq(struct device *dev)
return 0;
}
-static const struct dev_pm_ops mxcipu_pm_ops = {
- .suspend_noirq = ipu_suspend_noirq,
- .resume_noirq = ipu_resume_noirq,
-};
-
/*!
* This structure contains pointers to the power management callback functions.
*/
static struct platform_driver mxcipu_driver = {
.driver = {
.name = "imx-ipuv3",
- .pm = &mxcipu_pm_ops,
},
.probe = ipu_probe,
+ .suspend = ipu_suspend,
+ .resume = ipu_resume,
.remove = ipu_remove,
};
diff --git a/drivers/mxc/ipu3/ipu_device.c b/drivers/mxc/ipu3/ipu_device.c
index 6810cea8f503..8de791bbd118 100644
--- a/drivers/mxc/ipu3/ipu_device.c
+++ b/drivers/mxc/ipu3/ipu_device.c
@@ -351,6 +351,7 @@ static DECLARE_WAIT_QUEUE_HEAD(thread_waitq);
static DECLARE_WAIT_QUEUE_HEAD(res_waitq);
static atomic_t req_cnt;
static int major;
+static int max_ipu_no;
static int thread_id;
static atomic_t frame_no;
static struct class *ipu_class;
@@ -1147,7 +1148,7 @@ static int _get_vdoa_ipu_res(struct ipu_task_entry *t)
}
}
- for (i = 0; i < MXC_IPU_MAX_NUM; i++) {
+ for (i = 0; i < max_ipu_no; i++) {
ipu = ipu_get_soc(i);
if (IS_ERR(ipu))
BUG();
@@ -1176,7 +1177,7 @@ static int _get_vdoa_ipu_res(struct ipu_task_entry *t)
if (found_ipu)
goto next;
- for (i = 0; i < MXC_IPU_MAX_NUM; i++) {
+ for (i = 0; i < max_ipu_no; i++) {
ipu = ipu_get_soc(i);
if (IS_ERR(ipu))
BUG();
@@ -1704,7 +1705,7 @@ static int init_tiled_buf(struct ipu_soc *ipu, struct ipu_task_entry *t,
return -EINVAL;
else if (param.band_mode)
param.band_lines = (1 << t->set.band_lines);
- for (i = 0; i < MXC_IPU_MAX_NUM; i++) {
+ for (i = 0; i < max_ipu_no; i++) {
ipu_idx = ipu_get_soc(i);
if (!IS_ERR(ipu_idx) && ipu_idx == ipu)
break;
@@ -1712,7 +1713,7 @@ static int init_tiled_buf(struct ipu_soc *ipu, struct ipu_task_entry *t,
if (t->set.task & VDOA_ONLY)
/* dummy, didn't need ipu res */
i = 0;
- if (MXC_IPU_MAX_NUM == i) {
+ if (max_ipu_no == i) {
dev_err(t->dev, "ERR:[0x%p] get ipu num\n", t);
return -EINVAL;
}
@@ -1749,7 +1750,9 @@ static int init_tiled_buf(struct ipu_soc *ipu, struct ipu_task_entry *t,
return -EINVAL;
}
}
- vdoa_setup(t->vdoa_handle, &param);
+ ret = vdoa_setup(t->vdoa_handle, &param);
+ if (ret)
+ goto done;
vdoa_get_output_buf(t->vdoa_handle, &buf);
if (t->set.task & VDOA_ONLY)
goto done;
@@ -2883,7 +2886,7 @@ static void wait_split_task_complete(struct ipu_task_entry *parent,
out:
if (ret == -ETIMEDOUT) {
/* debug */
- for (k = 0; k < MXC_IPU_MAX_NUM; k++) {
+ for (k = 0; k < max_ipu_no; k++) {
ipu = ipu_get_soc(k);
if (IS_ERR(ipu)) {
BUG();
@@ -3407,6 +3410,7 @@ int register_ipu_device(struct ipu_soc *ipu, int id)
mutex_init(&ipu_ch_tbl.lock);
}
+ max_ipu_no = ++id;
ipu->rot_dma[0].size = 0;
ipu->rot_dma[1].size = 0;
diff --git a/drivers/mxc/ipu3/ipu_disp.c b/drivers/mxc/ipu3/ipu_disp.c
index d2fa65dd774e..4a774396ee99 100644
--- a/drivers/mxc/ipu3/ipu_disp.c
+++ b/drivers/mxc/ipu3/ipu_disp.c
@@ -174,44 +174,68 @@ static int _ipu_pixel_clk_set_parent(struct clk *clk, struct clk *parent)
#else
#define __INIT_CLK_DEBUG(n)
#endif
-struct clk ipu_pixel_clk[] = {
+struct clk ipu_pixel_clk[MXC_IPU_MAX_NUM][MXC_DI_NUM_PER_IPU] = {
{
- __INIT_CLK_DEBUG(pixel_clk_0)
- .id = 0,
- .get_rate = _ipu_pixel_clk_get_rate,
- .set_rate = _ipu_pixel_clk_set_rate,
- .round_rate = _ipu_pixel_clk_round_rate,
- .set_parent = _ipu_pixel_clk_set_parent,
- .enable = _ipu_pixel_clk_enable,
- .disable = _ipu_pixel_clk_disable,
+ {
+ __INIT_CLK_DEBUG(ipu1_pixel_clk_0)
+ .id = 0,
+ .get_rate = _ipu_pixel_clk_get_rate,
+ .set_rate = _ipu_pixel_clk_set_rate,
+ .round_rate = _ipu_pixel_clk_round_rate,
+ .set_parent = _ipu_pixel_clk_set_parent,
+ .enable = _ipu_pixel_clk_enable,
+ .disable = _ipu_pixel_clk_disable,
+ },
+ {
+ __INIT_CLK_DEBUG(ipu1_pixel_clk_1)
+ .id = 1,
+ .get_rate = _ipu_pixel_clk_get_rate,
+ .set_rate = _ipu_pixel_clk_set_rate,
+ .round_rate = _ipu_pixel_clk_round_rate,
+ .set_parent = _ipu_pixel_clk_set_parent,
+ .enable = _ipu_pixel_clk_enable,
+ .disable = _ipu_pixel_clk_disable,
+ },
},
{
- __INIT_CLK_DEBUG(pixel_clk_1)
- .id = 1,
- .get_rate = _ipu_pixel_clk_get_rate,
- .set_rate = _ipu_pixel_clk_set_rate,
- .round_rate = _ipu_pixel_clk_round_rate,
- .set_parent = _ipu_pixel_clk_set_parent,
- .enable = _ipu_pixel_clk_enable,
- .disable = _ipu_pixel_clk_disable,
+ {
+ __INIT_CLK_DEBUG(ipu2_pixel_clk_0)
+ .id = 0,
+ .get_rate = _ipu_pixel_clk_get_rate,
+ .set_rate = _ipu_pixel_clk_set_rate,
+ .round_rate = _ipu_pixel_clk_round_rate,
+ .set_parent = _ipu_pixel_clk_set_parent,
+ .enable = _ipu_pixel_clk_enable,
+ .disable = _ipu_pixel_clk_disable,
+ },
+ {
+ __INIT_CLK_DEBUG(ipu2_pixel_clk_1)
+ .id = 1,
+ .get_rate = _ipu_pixel_clk_get_rate,
+ .set_rate = _ipu_pixel_clk_set_rate,
+ .round_rate = _ipu_pixel_clk_round_rate,
+ .set_parent = _ipu_pixel_clk_set_parent,
+ .enable = _ipu_pixel_clk_enable,
+ .disable = _ipu_pixel_clk_disable,
+ },
},
};
-struct clk_lookup ipu_lookups[MXC_IPU_MAX_NUM][2] = {
+struct clk_lookup ipu_lookups[MXC_IPU_MAX_NUM][MXC_DI_NUM_PER_IPU] = {
{
{
- .con_id = "pixel_clk_0",
+ .con_id = "ipu1_pixel_clk_0",
},
{
- .con_id = "pixel_clk_1",
+ .con_id = "ipu1_pixel_clk_1",
},
},
{
{
- .con_id = "pixel_clk_0",
+ .con_id = "ipu2_pixel_clk_0",
},
{
- .con_id = "pixel_clk_1",
+ .con_id = "ipu2_pixel_clk_1",
},
},
};
diff --git a/drivers/mxc/ipu3/ipu_ic.c b/drivers/mxc/ipu3/ipu_ic.c
index e21d6134c84a..84dcbb145f6f 100644
--- a/drivers/mxc/ipu3/ipu_ic.c
+++ b/drivers/mxc/ipu3/ipu_ic.c
@@ -841,7 +841,7 @@ static bool _calc_resize_coeffs(struct ipu_soc *ipu,
where M = 2^13, SI - input size, SO - output size */
*resizeCoeff = (8192L * (tempSize - 1)) / (outSize - 1);
if (*resizeCoeff >= 16384L) {
- dev_err(ipu->dev, "Warning! Overflow on resize coeff.\n");
+ dev_dbg(ipu->dev, "Warning! Overflow on resize coeff.\n");
*resizeCoeff = 0x3FFF;
}
diff --git a/drivers/mxc/ipu3/ipu_prv.h b/drivers/mxc/ipu3/ipu_prv.h
index 526a2c0a36f0..7280dbaf00a8 100644
--- a/drivers/mxc/ipu3/ipu_prv.h
+++ b/drivers/mxc/ipu3/ipu_prv.h
@@ -20,16 +20,13 @@
#include <linux/interrupt.h>
#include <linux/fsl_devices.h>
-#ifdef CONFIG_MXC_IPU_V3H
-#define MXC_IPU_MAX_NUM 2
-#else
-#define MXC_IPU_MAX_NUM 1
-#endif
+#define MXC_IPU_MAX_NUM 2
+#define MXC_DI_NUM_PER_IPU 2
/* Globals */
extern int dmfc_type_setup;
-extern struct clk ipu_pixel_clk[];
-extern struct clk_lookup ipu_lookups[MXC_IPU_MAX_NUM][2];
+extern struct clk ipu_pixel_clk[MXC_IPU_MAX_NUM][MXC_DI_NUM_PER_IPU];
+extern struct clk_lookup ipu_lookups[MXC_IPU_MAX_NUM][MXC_DI_NUM_PER_IPU];
#define IDMA_CHAN_INVALID 0xFF
#define HIGH_RESOLUTION_WIDTH 1024
@@ -92,7 +89,7 @@ struct ipu_soc {
uint32_t channel_enable_mask;
/*use count*/
- atomic_t ipu_use_count;
+ int ipu_use_count;
int dc_use_count;
int dp_use_count;
int dmfc_use_count;
diff --git a/drivers/mxc/ipu3/vdoa.c b/drivers/mxc/ipu3/vdoa.c
index 5800fc606f8c..f9cba78e27b4 100644
--- a/drivers/mxc/ipu3/vdoa.c
+++ b/drivers/mxc/ipu3/vdoa.c
@@ -25,10 +25,10 @@
#include <linux/iram_alloc.h>
#include "vdoa.h"
-/* FIXME: use cmdline to specify the iram size */
/* 6band(3field* double buffer) * (width*2) * bandline(8)
= 6x1024x2x8 = 96k or 72k(1.5byte) */
-#define VDOA_IRAM_SIZE (1024*96)
+#define MAX_VDOA_IRAM_SIZE (1024*96)
+#define VDOA_IRAM_SIZE (1024*72)
#define VDOAC_BAND_HEIGHT_32LINES (32)
#define VDOAC_BAND_HEIGHT_16LINES (16)
@@ -123,6 +123,7 @@ struct vdoa_info {
};
static struct vdoa_info *g_vdoa;
+static unsigned long iram_size;
static DEFINE_MUTEX(vdoa_lock);
static inline void vdoa_read_register(struct vdoa_info *vdoa,
@@ -148,15 +149,16 @@ static void dump_registers(struct vdoa_info *vdoa)
vdoa_read_register(vdoa, i, &data);
}
-void vdoa_setup(vdoa_handle_t handle, struct vdoa_params *params)
+int vdoa_setup(vdoa_handle_t handle, struct vdoa_params *params)
{
int band_size;
+ int total_band_size = 0;
int ipu_stride;
u32 data;
struct vdoa_info *vdoa = (struct vdoa_info *)handle;
CHECK_NULL_PTR(vdoa);
- CHECK_STATE(VDOA_GET | VDOA_GET_OBUF | VDOA_STOP, return);
+ CHECK_STATE(VDOA_GET | VDOA_GET_OBUF | VDOA_STOP, return -EINVAL);
if (VDOA_GET == vdoa->state) {
dev_dbg(vdoa->dev, "w:%d, h:%d.\n",
params->width, params->height);
@@ -193,6 +195,12 @@ void vdoa_setup(vdoa_handle_t handle, struct vdoa_params *params)
band_size = ((params->width * 3) >> 1) *
params->band_lines;
if (params->interlaced) {
+ total_band_size = 6 * band_size; /* 3 frames*double buffer */
+ if (iram_size < total_band_size) {
+ dev_err(vdoa->dev, "iram_size:0x%lx is smaller than "
+ "request:0x%x!\n", iram_size, total_band_size);
+ return -EINVAL;
+ }
if (params->vfield_buf.prev_veba) {
if (params->band_mode) {
vdoa_write_register(vdoa, VDOAIEBA00,
@@ -265,6 +273,7 @@ void vdoa_setup(vdoa_handle_t handle, struct vdoa_params *params)
params->width * params->height);
}
vdoa->state = VDOA_SETUP;
+ return 0;
}
void vdoa_get_output_buf(vdoa_handle_t handle, struct vdoa_ipu_buf *buf)
@@ -330,7 +339,7 @@ void vdoa_stop(vdoa_handle_t handle)
struct vdoa_info *vdoa = (struct vdoa_info *)handle;
CHECK_NULL_PTR(vdoa);
- CHECK_STATE(VDOA_START | VDOA_INIRQ, return);
+ CHECK_STATE(VDOA_GET | VDOA_START | VDOA_INIRQ, return);
vdoa->state = VDOA_STOP;
disable_irq(vdoa->irq);
@@ -394,6 +403,21 @@ static irqreturn_t vdoa_irq_handler(int irq, void *data)
return IRQ_HANDLED;
}
+/* IRAM Size in Kbytes, example:vdoa_iram_size=64, 64KBytes */
+static int __init vdoa_iram_size_setup(char *options)
+{
+ int ret;
+
+ ret = strict_strtoul(options, 0, &iram_size);
+ if (ret)
+ iram_size = 0;
+ else
+ iram_size *= SZ_1K;
+
+ return 1;
+}
+__setup("vdoa_iram_size=", vdoa_iram_size_setup);
+
static int vdoa_probe(struct platform_device *pdev)
{
int ret;
@@ -451,16 +475,16 @@ static int vdoa_probe(struct platform_device *pdev)
ret = PTR_ERR(vdoa->clk);
goto err_clk;
}
-
- vdoa->iram_base = iram_alloc(VDOA_IRAM_SIZE, &vdoa->iram_paddr);
+ if ((iram_size == 0) || (iram_size > MAX_VDOA_IRAM_SIZE))
+ iram_size = VDOA_IRAM_SIZE;
+ vdoa->iram_base = iram_alloc(iram_size, &vdoa->iram_paddr);
if (!vdoa->iram_base) {
- dev_err(dev, "failed to get iram memory:0x%x\n",
- VDOA_IRAM_SIZE);
+ dev_err(dev, "failed to get iram memory:0x%lx\n", iram_size);
ret = -ENOMEM;
goto err_iram_alloc;
}
- dev_dbg(dev, "iram_base:0x%p,iram_paddr:0x%lx,size:0x%x\n",
- vdoa->iram_base, vdoa->iram_paddr, VDOA_IRAM_SIZE);
+ dev_dbg(dev, "iram_base:0x%p,iram_paddr:0x%lx,size:0x%lx\n",
+ vdoa->iram_base, vdoa->iram_paddr, iram_size);
vdoa->state = VDOA_INIT;
dev_set_drvdata(dev, vdoa);
@@ -491,7 +515,7 @@ static int __devexit vdoa_remove(struct platform_device *pdev)
clk_put(vdoa->clk);
clk_disable(vdoa->clk);
- iram_free(vdoa->iram_paddr, VDOA_IRAM_SIZE);
+ iram_free(vdoa->iram_paddr, iram_size);
iounmap(vdoa->reg_base);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
diff --git a/drivers/mxc/ipu3/vdoa.h b/drivers/mxc/ipu3/vdoa.h
index 82b0ee1f2c3c..23d72497a0a7 100644
--- a/drivers/mxc/ipu3/vdoa.h
+++ b/drivers/mxc/ipu3/vdoa.h
@@ -60,7 +60,7 @@ struct vdoa_ipu_buf {
struct vdoa_info;
typedef void *vdoa_handle_t;
-void vdoa_setup(vdoa_handle_t handle, struct vdoa_params *params);
+int vdoa_setup(vdoa_handle_t handle, struct vdoa_params *params);
void vdoa_get_output_buf(vdoa_handle_t handle, struct vdoa_ipu_buf *buf);
int vdoa_start(vdoa_handle_t handle, int timeout_ms);
void vdoa_stop(vdoa_handle_t handle);
diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index 27dcf755debc..467b5355de89 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -195,6 +195,7 @@ struct flexcan_priv {
struct clk *clk;
struct flexcan_platform_data *pdata;
enum flexcan_ip_version version;
+ int id;
};
static struct can_bittiming_const flexcan_bittiming_const = {
@@ -580,7 +581,8 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
if (reg_esr & FLEXCAN_ESR_WAK_INT) {
/* first clear stop request then wakeup irq status */
if (priv->version >= FLEXCAN_VER_10_0_12)
- mxc_iomux_set_gpr_register(13, 28, 1, 0);
+ /* CAN1/CAN2_STOP_REQ bit 28/29 in group 13 */
+ mxc_iomux_set_gpr_register(13, 28 + priv->id, 1, 0);
writel(FLEXCAN_ESR_WAK_INT, &regs->esr);
}
@@ -960,6 +962,9 @@ static int __devinit flexcan_probe(struct platform_device *pdev)
resource_size_t mem_size;
int err, irq;
+ if (pdev->id < 0)
+ return -ENODEV;
+
clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "no clock defined\n");
@@ -1007,6 +1012,7 @@ static int __devinit flexcan_probe(struct platform_device *pdev)
priv->base = base;
priv->dev = dev;
priv->clk = clk;
+ priv->id = pdev->id;
priv->pdata = pdev->dev.platform_data;
priv->version = pdev->id_entry->driver_data;
@@ -1074,7 +1080,8 @@ static int flexcan_suspend(struct platform_device *pdev, pm_message_t state)
/* enable stop request for wakeup */
if (priv->version >= FLEXCAN_VER_10_0_12)
- mxc_iomux_set_gpr_register(13, 28, 1, 1);
+ /* CAN1/CAN2_STOP_REQ bit 28/29 in group 13 */
+ mxc_iomux_set_gpr_register(13, 28 + priv->id, 1, 1);
ret = irq_set_irq_wake(dev->irq, 1);
if (ret)
diff --git a/drivers/net/fec.c b/drivers/net/fec.c
index 6e710e11f1a4..acb2965ae77d 100755
--- a/drivers/net/fec.c
+++ b/drivers/net/fec.c
@@ -71,17 +71,14 @@
#define FEC_QUIRK_SWAP_FRAME (1 << 1)
static struct platform_device_id fec_devtype[] = {
-#ifdef CONFIG_SOC_IMX6Q
{
- .name = DRIVER_NAME,
+ .name = "enet",
.driver_data = FEC_QUIRK_ENET_MAC,
},
-#else
{
- .name = DRIVER_NAME,
+ .name = "fec",
.driver_data = 0,
},
-#endif
{
.name = "imx28-fec",
.driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME,
@@ -146,12 +143,11 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
#define FEC_ENET_MII_CLK ((uint)2500000)
#define FEC_ENET_HOLD_TIME ((uint)0x100) /* 2 internal clock cycle*/
-#if defined(CONFIG_FEC_1588) && (defined(CONFIG_ARCH_MX28) || \
- defined(CONFIG_ARCH_MX6))
-#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII | \
- FEC_ENET_TS_AVAIL | FEC_ENET_TS_TIMER)
-#else
#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII)
+#if defined(CONFIG_FEC_1588)
+#define FEC_1588_IMASK (FEC_ENET_TS_AVAIL | FEC_ENET_TS_TIMER)
+#else
+#define FEC_1588_IMASK 0
#endif
/* The FEC stores dest/src/type, data, and checksum for receive packets.
@@ -1032,7 +1028,7 @@ static int fec_enet_mii_probe(struct net_device *ndev)
}
/* mask with MAC supported features */
- if (cpu_is_mx6())
+ if (cpu_is_mx6q() || cpu_is_mx6dl())
phy_dev->supported &= PHY_GBIT_FEATURES;
else
phy_dev->supported &= PHY_BASIC_FEATURES;
@@ -1091,7 +1087,7 @@ static int fec_enet_mii_init(struct platform_device *pdev)
(FEC_ENET_MII_CLK << 2)) << 1;
/* set hold time to 2 internal clock cycle */
- if (cpu_is_mx6())
+ if (cpu_is_mx6q() || cpu_is_mx6dl())
fep->phy_speed |= FEC_ENET_HOLD_TIME;
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
@@ -1546,14 +1542,14 @@ fec_restart(struct net_device *dev, int duplex)
udelay(10);
/*
- * enet-mac reset will reset mac address registers too,
- * so need to reconfigure it.
+ /* if uboot don't set MAC address, get MAC address
+ * from command line; if command line don't set MAC
+ * address, get from OCOTP; otherwise, allocate random
+ * address.
*/
- if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
- memcpy(&temp_mac, dev->dev_addr, ETH_ALEN);
- writel(cpu_to_be32(temp_mac[0]), fep->hwp + FEC_ADDR_LOW);
- writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH);
- }
+ memcpy(&temp_mac, dev->dev_addr, ETH_ALEN);
+ writel(cpu_to_be32(temp_mac[0]), fep->hwp + FEC_ADDR_LOW);
+ writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH);
/* Clear any outstanding interrupt. */
writel(0xffc00000, fep->hwp + FEC_IEVENT);
@@ -1623,23 +1619,24 @@ fec_restart(struct net_device *dev, int duplex)
val |= (1 << 9);
writel(val, fep->hwp + FEC_R_CNTRL);
+ }
- if (fep->ptimer_present) {
- /* Set Timer count */
- ret = fec_ptp_start(fep->ptp_priv);
- if (ret) {
- fep->ptimer_present = 0;
- reg = 0x0;
- } else
+ if (fep->ptimer_present) {
+ /* Set Timer count */
+ ret = fec_ptp_start(fep->ptp_priv);
+ if (ret) {
+ fep->ptimer_present = 0;
+ reg = 0x0;
+ } else
#if defined(CONFIG_SOC_IMX28) || defined(CONFIG_ARCH_MX6)
- reg = 0x00000010;
+ reg = 0x00000010;
#else
- reg = 0x0;
+ reg = 0x0;
#endif
} else
reg = 0x0;
-#ifdef FEC_MIIGSK_ENR
+ if (cpu_is_mx25() || cpu_is_mx53() || cpu_is_mx6sl()) {
if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) {
/* disable the gasket and wait */
writel(0, fep->hwp + FEC_MIIGSK_ENR);
@@ -1654,8 +1651,14 @@ fec_restart(struct net_device *dev, int duplex)
/* re-enable the gasket */
writel(2, fep->hwp + FEC_MIIGSK_ENR);
+ udelay(10);
+ if (!(readl(fep->hwp + FEC_MIIGSK_ENR) & 4)) {
+ udelay(100);
+ if (!(readl(fep->hwp + FEC_MIIGSK_ENR) & 4))
+ dev_err(&fep->pdev->dev,
+ "switch to RMII failed!\n");
+ }
}
-#endif
}
/* ENET enable */
@@ -1668,7 +1671,7 @@ fec_restart(struct net_device *dev, int duplex)
fep->phy_dev->speed == SPEED_1000)
val |= (0x1 << 5);
- if (cpu_is_mx6()) {
+ if (cpu_is_mx6q() || cpu_is_mx6dl()) {
/* enable endian swap */
val |= (0x1 << 8);
/* enable ENET store and forward mode */
@@ -1679,7 +1682,11 @@ fec_restart(struct net_device *dev, int duplex)
writel(0, fep->hwp + FEC_R_DES_ACTIVE);
/* Enable interrupts we wish to service */
- writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
+ if (cpu_is_mx6q() || cpu_is_mx6dl() || cpu_is_mx2() || cpu_is_mx3())
+ val = (FEC_1588_IMASK | FEC_DEFAULT_IMASK);
+ else
+ val = FEC_DEFAULT_IMASK;
+ writel(val, fep->hwp + FEC_IMASK);
}
static void
@@ -1699,7 +1706,7 @@ fec_stop(struct net_device *dev)
writel(1, fep->hwp + FEC_ECNTRL);
udelay(10);
- if (cpu_is_mx6())
+ if (cpu_is_mx6q() || cpu_is_mx6dl())
/* FIXME: we have to enable enet to keep mii interrupt works. */
writel(2, fep->hwp + FEC_ECNTRL);
@@ -1707,6 +1714,8 @@ fec_stop(struct net_device *dev)
if (fep->ptimer_present)
fec_ptp_stop(fep->ptp_priv);
writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
+ netif_stop_queue(dev);
+ fep->link = 0;
}
static int __devinit
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 8145ff85c8ab..6e53cce3c7fd 100755
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -219,6 +219,15 @@ config CHARGER_MAX8903
pins based on the status of charger connections with interrupt
handlers.
+config SABRESD_MAX8903
+ tristate "Sabresd Board Battery DC-DC Charger for USB and Adapter Power"
+ depends on GENERIC_HARDIRQS
+ depends on TOUCHSCREEN_MAX11801
+ help
+ Say Y to enable support for the MAX8903 DC-DC charger and sysfs on
+ sabresd board.The driver supports controlling charger and battery
+ based on the status of charger connections with interrupt handlers.
+
config CHARGER_TWL4030
tristate "OMAP TWL4030 BCI charger driver"
depends on TWL4030_CORE
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index ad6493affec8..1f359ba8a03c 100755
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -37,3 +37,4 @@ obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o
obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o
obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o
obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o
+obj-$(CONFIG_SABRESD_MAX8903) += sabresd_battery.o
diff --git a/drivers/power/max8903_battery.c b/drivers/power/max8903_battery.c
new file mode 100755
index 000000000000..84b5373cbb2f
--- /dev/null
+++ b/drivers/power/max8903_battery.c
@@ -0,0 +1,748 @@
+/*
+ * max8903_battery.c - Maxim 8903 USB/Adapter Charger Driver
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Copyright (C) 2011-2012 Freescale Semiconductor, Inc.
+ * Based on max8903_charger.c
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/power_supply.h>
+#include <linux/platform_device.h>
+#include <linux/power/max8903_battery.h>
+#include <linux/sort.h>
+
+
+#define BATTERY_UPDATE_INTERVAL 5 /*seconds*/
+#define LOW_VOLT_THRESHOLD 2800000
+#define HIGH_VOLT_THRESHOLD 4200000
+#define ADC_SAMPLE_COUNT 4
+
+struct max8903_data {
+ struct max8903_pdata *pdata;
+ struct device *dev;
+ struct power_supply psy;
+ bool fault;
+ bool usb_in;
+ bool ta_in;
+ bool chg_state;
+ struct delayed_work work;
+ unsigned int interval;
+ unsigned short thermal_raw;
+ int voltage_uV;
+ int current_uA;
+ int battery_status;
+ int charger_online;
+ int charger_voltage_uV;
+ int real_capacity;
+ int percent;
+ int old_percent;
+ struct power_supply bat;
+ struct mutex work_lock;
+};
+
+typedef struct {
+ u32 voltage;
+ u32 percent;
+} battery_capacity , *pbattery_capacity;
+
+static bool capacity_changed_flag;
+
+static battery_capacity chargingTable[] = {
+ {4148, 100},
+ {4126, 99},
+ {4123, 98},
+ {4120, 97},
+ {4105, 96},
+ {4090, 96},
+ {4075, 95},
+ {4060, 94},
+ {4045, 93},
+ {4030, 92},
+ {4015, 91},
+ {4000, 90},
+ {3900, 85},
+ {3790, 80},
+ {3760, 75},
+ {3730, 70},
+ {3700, 65},
+ {3680, 60},
+ {3660, 55},
+ {3640, 50},
+ {3600, 45},
+ {3550, 40},
+ {3510, 35},
+ {3450, 30},
+ {3310, 25},
+ {3240, 20},
+ {3180, 15},
+ {3030, 10},
+ {2820, 5},
+ {2800, 0},
+ {0, 0}
+};
+static battery_capacity dischargingTable[] = {
+ {4100, 100},
+ {4090, 99},
+ {4080, 98},
+ {4060, 97},
+ {4040, 96},
+ {3920, 96},
+ {3900, 95},
+ {3970, 94},
+ {3940, 93},
+ {3910, 92},
+ {3890, 91},
+ {3860, 90},
+ {3790, 85},
+ {3690, 80},
+ {3660, 75},
+ {3630, 70},
+ {3600, 65},
+ {3580, 60},
+ {3560, 55},
+ {3540, 50},
+ {3500, 45},
+ {3450, 40},
+ {3410, 35},
+ {3350, 30},
+ {3310, 25},
+ {3240, 20},
+ {3180, 15},
+ {3030, 10},
+ {2820, 5},
+ {2800, 0},
+ {0, 0}
+};
+
+u32 calibrate_battery_capability_percent(struct max8903_data *data)
+{
+ u8 i;
+ pbattery_capacity pTable;
+ u32 tableSize;
+ if (data->battery_status == POWER_SUPPLY_STATUS_DISCHARGING) {
+ pTable = dischargingTable;
+ tableSize = sizeof(dischargingTable)/sizeof(dischargingTable[0]);
+ } else {
+ pTable = chargingTable;
+ tableSize = sizeof(chargingTable)/sizeof(chargingTable[0]);
+ }
+ for (i = 0; i < tableSize; i++) {
+ if (data->voltage_uV >= pTable[i].voltage)
+ return pTable[i].percent;
+ }
+
+ return 0;
+}
+
+static enum power_supply_property max8903_charger_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_HEALTH,
+};
+
+static enum power_supply_property max8903_battery_props[] = {
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+};
+
+extern u32 max11801_read_adc(void);
+
+static void max8903_charger_update_status(struct max8903_data *data)
+{
+ if (data->usb_in || data->ta_in) {
+ data->charger_online = 1;
+ } else {
+ data->charger_online = 0;
+ }
+ if (data->charger_online == 0) {
+ data->battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
+ } else {
+ if (data->pdata->chg) {
+ if (gpio_get_value(data->pdata->chg) == 0) {
+ data->battery_status = POWER_SUPPLY_STATUS_CHARGING;
+ } else if ((data->usb_in || data->ta_in) &&
+ gpio_get_value(data->pdata->chg) == 1) {
+ data->battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ }
+ }
+ }
+ pr_debug("chg: %d \n", gpio_get_value(data->pdata->chg));
+}
+static int cmp_func(const void *_a, const void *_b)
+{
+ const int *a = _a, *b = _b;
+
+ if (*a > *b)
+ return 1;
+ if (*a < *b)
+ return -1;
+ return 0;
+}
+u32 calibration_voltage(struct max8903_data *data)
+{
+ int volt[ADC_SAMPLE_COUNT];
+ u32 voltage_data;
+ int i;
+ for (i = 0; i < ADC_SAMPLE_COUNT; i++) {
+ if (data->charger_online == 0) {
+ /* ADC offset when battery is discharger*/
+ volt[i] = max11801_read_adc()-1880;
+ } else {
+ volt[i] = max11801_read_adc()-1960;
+ }
+ }
+ sort(volt, i, 4, cmp_func, NULL);
+ for (i = 0; i < ADC_SAMPLE_COUNT; i++)
+ pr_debug("volt_sorted[%2d]: %d\n", i, volt[i]);
+ /* get the average of second max/min of remained. */
+ voltage_data = (volt[1] + volt[i - 2]) / 2;
+ return voltage_data;
+}
+
+static void max8903_battery_update_status(struct max8903_data *data)
+{
+ static int counter;
+#if 0
+ data->voltage_uV = max11801_read_adc();
+#endif
+ mutex_lock(&data->work_lock);
+ data->voltage_uV = calibration_voltage(data);
+ data->percent = calibrate_battery_capability_percent(data);
+ if (data->percent != data->old_percent) {
+ data->old_percent = data->percent;
+ capacity_changed_flag = true;
+ }
+ if ((capacity_changed_flag == true) && (data->charger_online)) {
+ counter++;
+ if (counter > 2) {
+ counter = 0;
+ capacity_changed_flag = false;
+ power_supply_changed(&data->bat);
+ }
+ }
+ mutex_unlock(&data->work_lock);
+}
+
+static int max8903_battery_get_property(struct power_supply *bat,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct max8903_data *di = container_of(bat,
+ struct max8903_data, bat);
+ static unsigned long last;
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ if (di->pdata->chg) {
+ if (gpio_get_value(di->pdata->chg) == 0) {
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ } else if ((di->usb_in || di->ta_in) && gpio_get_value(di->pdata->chg) == 1) {
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ } else {
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ }
+ }
+ di->battery_status = val->intval;
+ return 0;
+ default:
+ break;
+ }
+ if (!last || time_after(jiffies, last + HZ / 2)) {
+ last = jiffies;
+ max8903_charger_update_status(di);
+ max8903_battery_update_status(di);
+ }
+ switch (psp) {
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = di->voltage_uV;
+ break;
+#if 0
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = di->current_uA;
+ break;
+#endif
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = 0;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = HIGH_VOLT_THRESHOLD;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ val->intval = LOW_VOLT_THRESHOLD;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = 1;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = di->percent < 0 ? 0 :
+ (di->percent > 100 ? 100 : di->percent);
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = 0;
+ if (di->usb_in || di->ta_in)
+ val->intval = 1;
+ di->charger_online = val->intval;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+static int max8903_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct max8903_data *data = container_of(psy,
+ struct max8903_data, psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ if (data->pdata->chg) {
+ if (gpio_get_value(data->pdata->chg) == 0) {
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ }
+ else if ((data->usb_in || data->ta_in) &&
+ gpio_get_value(data->pdata->chg) == 1) {
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ } else {
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ }
+ }
+ data->battery_status = val->intval;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = 0;
+ if (data->usb_in || data->ta_in)
+ val->intval = 1;
+ data->charger_online = val->intval;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ if (data->fault)
+ val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static irqreturn_t max8903_dcin(int irq, void *_data)
+{
+ struct max8903_data *data = _data;
+ struct max8903_pdata *pdata = data->pdata;
+ bool ta_in;
+ enum power_supply_type old_type;
+
+ ta_in = gpio_get_value(pdata->dok) ? false : true;
+
+ if (ta_in == data->ta_in)
+ return IRQ_HANDLED;
+
+ data->ta_in = ta_in;
+#if 0
+ /* Set Current-Limit-Mode 1:DC 0:USB */
+ if (pdata->dcm)
+ gpio_set_value(pdata->dcm, ta_in ? 1 : 0);
+ /* Charger Enable / Disable (cen is negated) */
+ if (pdata->cen)
+ gpio_set_value(pdata->cen, ta_in ? 0 :
+ (data->usb_in ? 0 : 1));
+#endif
+ pr_info("TA(DC-IN) Charger %s.\n", ta_in ?
+ "Connected" : "Disconnected");
+
+ old_type = data->psy.type;
+
+ if (data->ta_in)
+ data->psy.type = POWER_SUPPLY_TYPE_MAINS;
+ else if (data->usb_in)
+ data->psy.type = POWER_SUPPLY_TYPE_USB;
+ else
+ data->psy.type = POWER_SUPPLY_TYPE_BATTERY;
+
+ if (old_type != data->psy.type) {
+ power_supply_changed(&data->psy);
+ power_supply_changed(&data->bat);
+ }
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t max8903_usbin(int irq, void *_data)
+{
+ struct max8903_data *data = _data;
+ struct max8903_pdata *pdata = data->pdata;
+ bool usb_in;
+ enum power_supply_type old_type;
+
+ usb_in = gpio_get_value(pdata->uok) ? false : true;
+
+ if (usb_in == data->usb_in)
+ return IRQ_HANDLED;
+
+ data->usb_in = usb_in;
+
+#if 0
+ /* Do not touch Current-Limit-Mode */
+
+ /* Charger Enable / Disable (cen is negated) */
+ if (pdata->cen)
+ gpio_set_value(pdata->cen, usb_in ? 0 :
+ (data->ta_in ? 0 : 1));
+#endif
+ pr_info("USB Charger %s.\n", usb_in ?
+ "Connected" : "Disconnected");
+
+ old_type = data->psy.type;
+
+ if (data->ta_in)
+ data->psy.type = POWER_SUPPLY_TYPE_MAINS;
+ else if (data->usb_in)
+ data->psy.type = POWER_SUPPLY_TYPE_USB;
+ else
+ data->psy.type = POWER_SUPPLY_TYPE_BATTERY;
+
+ if (old_type != data->psy.type) {
+ power_supply_changed(&data->psy);
+ power_supply_changed(&data->bat);
+ }
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t max8903_fault(int irq, void *_data)
+{
+ struct max8903_data *data = _data;
+ struct max8903_pdata *pdata = data->pdata;
+ bool fault;
+
+ fault = gpio_get_value(pdata->flt) ? false : true;
+
+ if (fault == data->fault)
+ return IRQ_HANDLED;
+
+ data->fault = fault;
+
+ if (fault)
+ dev_err(data->dev, "Charger suffers a fault and stops.\n");
+ else
+ dev_err(data->dev, "Charger recovered from a fault.\n");
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t max8903_chg(int irq, void *_data)
+{
+ struct max8903_data *data = _data;
+ struct max8903_pdata *pdata = data->pdata;
+ int chg_state;
+
+ chg_state = gpio_get_value(pdata->chg) ? false : true;
+
+ if (chg_state == data->chg_state)
+ return IRQ_HANDLED;
+
+ data->chg_state = chg_state;
+#if 0
+ if (chg_state)
+ pr_info("Charger stop.\n ");
+ else
+ pr_info("Charger start.\n ");
+#endif
+ return IRQ_HANDLED;
+}
+
+static void max8903_battery_work(struct work_struct *work)
+{
+ struct max8903_data *data;
+ data = container_of(work, struct max8903_data, work.work);
+ data->interval = HZ * BATTERY_UPDATE_INTERVAL;
+
+ max8903_charger_update_status(data);
+ max8903_battery_update_status(data);
+
+ pr_debug("battery voltage: %4d mV\n" , data->voltage_uV);
+ pr_debug("charger online status: %d\n" , data->charger_online);
+ pr_debug("battery status : %d\n" , data->battery_status);
+ pr_debug("battery capacity percent: %3d\n" , data->percent);
+ pr_debug("data->usb_in: %x , data->ta_in: %x \n" , data->usb_in, data->ta_in);
+ /* reschedule for the next time */
+ schedule_delayed_work(&data->work, data->interval);
+}
+static __devinit int max8903_probe(struct platform_device *pdev)
+{
+ struct max8903_data *data;
+ struct device *dev = &pdev->dev;
+ struct max8903_pdata *pdata = pdev->dev.platform_data;
+ int ret = 0;
+ int gpio = 0;
+ int ta_in = 0;
+ int usb_in = 0;
+ int retval;
+
+ data = kzalloc(sizeof(struct max8903_data), GFP_KERNEL);
+ if (data == NULL) {
+ dev_err(dev, "Cannot allocate memory.\n");
+ return -ENOMEM;
+ }
+ data->pdata = pdata;
+ data->dev = dev;
+
+ platform_set_drvdata(pdev, data);
+ capacity_changed_flag = false;
+ data->usb_in = 0;
+ data->ta_in = 0;
+
+ if (pdata->dc_valid == false && pdata->usb_valid == false) {
+ dev_err(dev, "No valid power sources.\n");
+ printk(KERN_INFO "No valid power sources.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ if (pdata->dc_valid) {
+#if 0
+ if (pdata->dok && gpio_is_valid(pdata->dok) &&
+ pdata->dcm && gpio_is_valid(pdata->dcm)) {
+#endif
+ if (pdata->dok && gpio_is_valid(pdata->dok)) {
+ gpio = pdata->dok; /* PULL_UPed Interrupt */
+ ta_in = gpio_get_value(gpio) ? 0 : 1;
+#if 0
+ gpio = pdata->dcm; /* Output */
+ gpio_set_value(gpio, ta_in);
+#endif
+ } else if (pdata->dok && gpio_is_valid(pdata->dok) && pdata->dcm_always_high) {
+ ta_in = pdata->dok; /* PULL_UPed Interrupt */
+ ta_in = gpio_get_value(gpio) ? 0 : 1;
+ } else {
+ dev_err(dev, "When DC is wired, DOK and DCM should"
+ " be wired as well."
+ " or set dcm always high\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+ if (pdata->usb_valid) {
+ if (pdata->uok && gpio_is_valid(pdata->uok)) {
+ gpio = pdata->uok;
+ usb_in = gpio_get_value(gpio) ? 0 : 1;
+ } else {
+ dev_err(dev, "When USB is wired, UOK should be wired."
+ "as well.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+ if (pdata->chg) {
+ if (!gpio_is_valid(pdata->chg)) {
+ dev_err(dev, "Invalid pin: chg.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+
+ if (pdata->flt) {
+ if (!gpio_is_valid(pdata->flt)) {
+ dev_err(dev, "Invalid pin: flt.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+
+ if (pdata->usus) {
+ if (!gpio_is_valid(pdata->usus)) {
+ dev_err(dev, "Invalid pin: usus.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+ mutex_init(&data->work_lock);
+ data->fault = false;
+ data->ta_in = ta_in;
+ data->usb_in = usb_in;
+ data->psy.name = "max8903-ac";
+ data->psy.type = (ta_in) ? POWER_SUPPLY_TYPE_MAINS :
+ ((usb_in) ? POWER_SUPPLY_TYPE_USB :
+ POWER_SUPPLY_TYPE_BATTERY);
+ data->psy.get_property = max8903_get_property;
+ data->psy.properties = max8903_charger_props;
+ data->psy.num_properties = ARRAY_SIZE(max8903_charger_props);
+ ret = power_supply_register(dev, &data->psy);
+ if (ret) {
+ dev_err(dev, "failed: power supply register.\n");
+ goto err_psy;
+ }
+ data->bat.name = "max8903-charger";
+ data->bat.type = POWER_SUPPLY_TYPE_BATTERY;
+ data->bat.properties = max8903_battery_props;
+ data->bat.num_properties = ARRAY_SIZE(max8903_battery_props);
+ data->bat.get_property = max8903_battery_get_property;
+ data->bat.use_for_apm = 1;
+ retval = power_supply_register(&pdev->dev, &data->bat);
+ if (retval) {
+ dev_err(data->dev, "failed to register battery\n");
+ goto battery_failed;
+ }
+ INIT_DELAYED_WORK(&data->work, max8903_battery_work);
+ schedule_delayed_work(&data->work, data->interval);
+ if (pdata->dc_valid) {
+ ret = request_threaded_irq(gpio_to_irq(pdata->dok),
+ NULL, max8903_dcin,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "MAX8903 DC IN", data);
+ if (ret) {
+ dev_err(dev, "Cannot request irq %d for DC (%d)\n",
+ gpio_to_irq(pdata->dok), ret);
+ goto err_usb_irq;
+ }
+ }
+
+ if (pdata->usb_valid) {
+ ret = request_threaded_irq(gpio_to_irq(pdata->uok),
+ NULL, max8903_usbin,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "MAX8903 USB IN", data);
+ if (ret) {
+ dev_err(dev, "Cannot request irq %d for USB (%d)\n",
+ gpio_to_irq(pdata->uok), ret);
+ goto err_dc_irq;
+ }
+ }
+
+ if (pdata->flt) {
+ ret = request_threaded_irq(gpio_to_irq(pdata->flt),
+ NULL, max8903_fault,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "MAX8903 Fault", data);
+ if (ret) {
+ dev_err(dev, "Cannot request irq %d for Fault (%d)\n",
+ gpio_to_irq(pdata->flt), ret);
+ goto err_flt_irq;
+ }
+ }
+
+ if (pdata->chg) {
+ ret = request_threaded_irq(gpio_to_irq(pdata->chg),
+ NULL, max8903_chg,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "MAX8903 Fault", data);
+ if (ret) {
+ dev_err(dev, "Cannot request irq %d for Fault (%d)\n",
+ gpio_to_irq(pdata->flt), ret);
+ goto err_chg_irq;
+ }
+ }
+ return 0;
+err_psy:
+ power_supply_unregister(&data->psy);
+battery_failed:
+ power_supply_unregister(&data->bat);
+err_usb_irq:
+ if (pdata->usb_valid)
+ free_irq(gpio_to_irq(pdata->uok), data);
+ cancel_delayed_work(&data->work);
+err_dc_irq:
+ if (pdata->dc_valid)
+ free_irq(gpio_to_irq(pdata->dok), data);
+ cancel_delayed_work(&data->work);
+err_flt_irq:
+ if (pdata->usb_valid)
+ free_irq(gpio_to_irq(pdata->uok), data);
+ cancel_delayed_work(&data->work);
+err_chg_irq:
+ if (pdata->dc_valid)
+ free_irq(gpio_to_irq(pdata->dok), data);
+ cancel_delayed_work(&data->work);
+err:
+ kfree(data);
+ return ret;
+}
+
+static __devexit int max8903_remove(struct platform_device *pdev)
+{
+ struct max8903_data *data = platform_get_drvdata(pdev);
+ if (data) {
+ struct max8903_pdata *pdata = data->pdata;
+ if (pdata->flt)
+ free_irq(gpio_to_irq(pdata->flt), data);
+ if (pdata->usb_valid)
+ free_irq(gpio_to_irq(pdata->uok), data);
+ if (pdata->dc_valid)
+ free_irq(gpio_to_irq(pdata->dok), data);
+ if (pdata->dc_valid)
+ free_irq(gpio_to_irq(pdata->chg), data);
+ cancel_delayed_work_sync(&data->work);
+ power_supply_unregister(&data->psy);
+ power_supply_unregister(&data->bat);
+ kfree(data);
+ }
+ return 0;
+}
+
+static int max8903_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct max8903_data *data = platform_get_drvdata(pdev);
+
+ cancel_delayed_work(&data->work);
+ return 0;
+}
+
+static int max8903_resume(struct platform_device *pdev)
+{
+ struct max8903_data *data = platform_get_drvdata(pdev);
+
+ schedule_delayed_work(&data->work, BATTERY_UPDATE_INTERVAL);
+ return 0;
+
+}
+
+static struct platform_driver max8903_driver = {
+ .probe = max8903_probe,
+ .remove = __devexit_p(max8903_remove),
+ .suspend = max8903_suspend,
+ .resume = max8903_resume,
+ .driver = {
+ .name = "max8903-charger",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init max8903_init(void)
+{
+ return platform_driver_register(&max8903_driver);
+}
+module_init(max8903_init);
+
+static void __exit max8903_exit(void)
+{
+ platform_driver_unregister(&max8903_driver);
+}
+module_exit(max8903_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MAX8903 Battery Driver");
+MODULE_ALIAS("max8903_battery");
diff --git a/drivers/power/max8903_charger.c b/drivers/power/max8903_charger.c
index 492df4310228..fee8191767f0 100644
--- a/drivers/power/max8903_charger.c
+++ b/drivers/power/max8903_charger.c
@@ -246,13 +246,13 @@ static __devinit int max8903_probe(struct platform_device *pdev)
gpio = pdata->dok; /* PULL_UPed Interrupt */
error = gpio_request(gpio, "chg_dc");
- error = gpio_direction_input(gpio);
if (error < 0) {
dev_err(dev, "failed to configure"
" request/direction for GPIO %d, error %d\n",
gpio, error);
goto err;
}
+ gpio_direction_input(gpio);
ta_in = gpio_get_value(gpio) ? 0 : 1;
@@ -283,15 +283,15 @@ static __devinit int max8903_probe(struct platform_device *pdev)
if (pdata->uok && gpio_is_valid(pdata->uok)) {
gpio = pdata->uok;
error = gpio_request(gpio, "chg_usb");
- error = gpio_direction_input(gpio);
if (error < 0) {
dev_err(dev, "failed to configure"
" request/direction for GPIO %d, error %d\n",
gpio, error);
goto err;
}
- usb_in = gpio_get_value(gpio) ? 0 : 1;
+ gpio_direction_input(gpio);
+ usb_in = gpio_get_value(gpio) ? 0 : 1;
if (usb_in)
data->usb_in = true;
else
@@ -321,13 +321,13 @@ static __devinit int max8903_probe(struct platform_device *pdev)
goto err;
}
error = gpio_request(pdata->chg, "chg_status");
- error = gpio_direction_input(pdata->chg);
if (error < 0) {
dev_err(dev, "failed to configure"
" request/direction for GPIO %d, error %d\n",
pdata->chg, error);
goto err;
}
+ error = gpio_direction_input(pdata->chg);
}
if (pdata->flt) {
@@ -337,13 +337,13 @@ static __devinit int max8903_probe(struct platform_device *pdev)
goto err;
}
error = gpio_request(pdata->flt, "chg_fault");
- error = gpio_direction_input(pdata->flt);
if (error < 0) {
dev_err(dev, "failed to configure"
" request/direction for GPIO %d, error %d\n",
pdata->flt, error);
goto err;
}
+ error = gpio_direction_input(pdata->flt);
}
if (pdata->usus) {
diff --git a/drivers/power/sabresd_battery.c b/drivers/power/sabresd_battery.c
new file mode 100755
index 000000000000..75d07b178083
--- /dev/null
+++ b/drivers/power/sabresd_battery.c
@@ -0,0 +1,701 @@
+/*
+ * sabresd_battery.c - Maxim 8903 USB/Adapter Charger Driver
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Copyright (C) 2011-2012 Freescale Semiconductor, Inc.
+ * Based on max8903_charger.c
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/power_supply.h>
+#include <linux/platform_device.h>
+#include <linux/power/sabresd_battery.h>
+#include <linux/sort.h>
+
+
+#define BATTERY_UPDATE_INTERVAL 5 /*seconds*/
+#define LOW_VOLT_THRESHOLD 2800000
+#define HIGH_VOLT_THRESHOLD 4200000
+#define ADC_SAMPLE_COUNT 6
+
+struct max8903_data {
+ struct max8903_pdata *pdata;
+ struct device *dev;
+ struct power_supply psy;
+ bool fault;
+ bool usb_in;
+ bool ta_in;
+ bool chg_state;
+ struct delayed_work work;
+ unsigned int interval;
+ unsigned short thermal_raw;
+ int voltage_uV;
+ int current_uA;
+ int battery_status;
+ int charger_online;
+ int charger_voltage_uV;
+ int real_capacity;
+ int percent;
+ int old_percent;
+ struct power_supply bat;
+ struct mutex work_lock;
+};
+
+typedef struct {
+ u32 voltage;
+ u32 percent;
+} battery_capacity , *pbattery_capacity;
+
+static bool capacity_changed_flag;
+
+static battery_capacity chargingTable[] = {
+ {4146, 100},
+ {4133, 99},
+ {4123, 98},
+ {4115, 97},
+ {4090, 96},
+ {4075, 95},
+ {4060, 94},
+ {4045, 93},
+ {4030, 92},
+ {4015, 91},
+ {4000, 90},
+ {3900, 85},
+ {3790, 80},
+ {3760, 75},
+ {3730, 70},
+ {3700, 65},
+ {3680, 60},
+ {3660, 55},
+ {3640, 50},
+ {3600, 45},
+ {3550, 40},
+ {3510, 35},
+ {3450, 30},
+ {3310, 25},
+ {3240, 20},
+ {3180, 15},
+ {3030, 10},
+ {2820, 5},
+ {2800, 0},
+ {0, 0}
+};
+static battery_capacity dischargingTable[] = {
+ {4110, 100},
+ {4020, 99},
+ {3950, 98},
+ {3920, 97},
+ {3890, 96},
+ {3860, 96},
+ {3830, 95},
+ {3800, 94},
+ {3760, 93},
+ {3730, 92},
+ {3700, 91},
+ {3670, 90},
+ {3630, 85},
+ {3600, 80},
+ {3570, 75},
+ {3545, 70},
+ {3515, 65},
+ {3480, 60},
+ {3445, 55},
+ {3410, 50},
+ {3375, 45},
+ {3340, 40},
+ {3300, 35},
+ {3250, 30},
+ {3200, 25},
+ {3150, 20},
+ {3090, 15},
+ {3020, 10},
+ {2820, 5},
+ {2800, 0},
+ {0, 0}
+};
+
+u32 calibrate_battery_capability_percent(struct max8903_data *data)
+{
+ u8 i;
+ pbattery_capacity pTable;
+ u32 tableSize;
+ if (data->battery_status == POWER_SUPPLY_STATUS_DISCHARGING) {
+ pTable = dischargingTable;
+ tableSize = sizeof(dischargingTable)/sizeof(dischargingTable[0]);
+ } else {
+ pTable = chargingTable;
+ tableSize = sizeof(chargingTable)/sizeof(chargingTable[0]);
+ }
+ for (i = 0; i < tableSize; i++) {
+ if (data->voltage_uV >= pTable[i].voltage)
+ return pTable[i].percent;
+ }
+
+ return 0;
+}
+
+static enum power_supply_property max8903_charger_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static enum power_supply_property max8903_battery_props[] = {
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_HEALTH,
+};
+
+extern u32 max11801_read_adc(void);
+
+static void max8903_charger_update_status(struct max8903_data *data)
+{
+ if (data->usb_in || data->ta_in)
+ data->charger_online = 1;
+ else
+ data->charger_online = 0;
+ if (data->charger_online == 0) {
+ data->battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
+ } else {
+ if (data->pdata->chg) {
+ if (gpio_get_value(data->pdata->chg) == 0) {
+ data->battery_status = POWER_SUPPLY_STATUS_CHARGING;
+ } else if ((data->usb_in || data->ta_in) &&
+ gpio_get_value(data->pdata->chg) == 1) {
+ if (data->percent == 100)
+ data->battery_status = POWER_SUPPLY_STATUS_FULL;
+ else
+ data->battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ }
+ }
+ }
+ pr_debug("chg: %d \n", gpio_get_value(data->pdata->chg));
+}
+static int cmp_func(const void *_a, const void *_b)
+{
+ const int *a = _a, *b = _b;
+
+ if (*a > *b)
+ return 1;
+ if (*a < *b)
+ return -1;
+ return 0;
+}
+u32 calibration_voltage(struct max8903_data *data)
+{
+ int volt[ADC_SAMPLE_COUNT];
+ u32 voltage_data;
+ int i;
+ for (i = 0; i < ADC_SAMPLE_COUNT; i++) {
+ if (data->charger_online == 0) {
+ /* ADC offset when battery is discharger*/
+ volt[i] = max11801_read_adc()-1494;
+ } else {
+ volt[i] = max11801_read_adc()-1545;
+ }
+ }
+ sort(volt, i, 4, cmp_func, NULL);
+ for (i = 0; i < ADC_SAMPLE_COUNT; i++)
+ pr_debug("volt_sorted[%2d]: %d\n", i, volt[i]);
+ /* get the average of second max/min of remained. */
+ voltage_data = (volt[2] + volt[ADC_SAMPLE_COUNT - 3]) / 2;
+ return voltage_data;
+}
+
+static void max8903_battery_update_status(struct max8903_data *data)
+{
+ static int counter;
+ mutex_lock(&data->work_lock);
+ data->voltage_uV = calibration_voltage(data);
+ data->percent = calibrate_battery_capability_percent(data);
+ if (data->percent != data->old_percent) {
+ data->old_percent = data->percent;
+ capacity_changed_flag = true;
+ }
+ if ((capacity_changed_flag == true) && (data->charger_online)) {
+ counter++;
+ if (counter > 2) {
+ counter = 0;
+ capacity_changed_flag = false;
+ power_supply_changed(&data->bat);
+ }
+ }
+ mutex_unlock(&data->work_lock);
+}
+
+static int max8903_battery_get_property(struct power_supply *bat,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct max8903_data *di = container_of(bat,
+ struct max8903_data, bat);
+ static unsigned long last;
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ if (di->pdata->chg) {
+ if ((di->usb_in || di->ta_in) && gpio_get_value(di->pdata->chg) == 0) {
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ } else if ((di->usb_in || di->ta_in) && gpio_get_value(di->pdata->chg) == 1) {
+ if (di->percent == 100)
+ di->battery_status = POWER_SUPPLY_STATUS_FULL;
+ else
+ di->battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ }
+ else {
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ }
+ }
+ di->battery_status = val->intval;
+ return 0;
+ default:
+ break;
+ }
+ if (!last || time_after(jiffies, last + HZ / 2)) {
+ last = jiffies;
+ max8903_charger_update_status(di);
+ max8903_battery_update_status(di);
+ }
+ switch (psp) {
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = di->voltage_uV;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = 0;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = HIGH_VOLT_THRESHOLD;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ val->intval = LOW_VOLT_THRESHOLD;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = 1;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = di->percent < 0 ? 0 :
+ (di->percent > 100 ? 100 : di->percent);
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ if (di->fault)
+ val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+static int max8903_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct max8903_data *data = container_of(psy,
+ struct max8903_data, psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = 0;
+ if (data->usb_in || data->ta_in)
+ val->intval = 1;
+ data->charger_online = val->intval;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static irqreturn_t max8903_dcin(int irq, void *_data)
+{
+ struct max8903_data *data = _data;
+ struct max8903_pdata *pdata = data->pdata;
+ bool ta_in;
+
+ ta_in = gpio_get_value(pdata->dok) ? false : true;
+
+ if (ta_in == data->ta_in)
+ return IRQ_HANDLED;
+
+ data->ta_in = ta_in;
+ pr_info("TA(DC-IN) Charger %s.\n", ta_in ?
+ "Connected" : "Disconnected");
+ power_supply_changed(&data->psy);
+ power_supply_changed(&data->bat);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t max8903_usbin(int irq, void *_data)
+{
+ struct max8903_data *data = _data;
+ struct max8903_pdata *pdata = data->pdata;
+ bool usb_in;
+
+ usb_in = gpio_get_value(pdata->uok) ? false : true;
+
+ if (usb_in == data->usb_in)
+ return IRQ_HANDLED;
+
+ data->usb_in = usb_in;
+
+ pr_info("USB Charger %s.\n", usb_in ?
+ "Connected" : "Disconnected");
+ power_supply_changed(&data->psy);
+ power_supply_changed(&data->bat);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t max8903_fault(int irq, void *_data)
+{
+ struct max8903_data *data = _data;
+ struct max8903_pdata *pdata = data->pdata;
+ bool fault;
+
+ fault = gpio_get_value(pdata->flt) ? false : true;
+
+ if (fault == data->fault)
+ return IRQ_HANDLED;
+
+ data->fault = fault;
+
+ if (fault)
+ dev_err(data->dev, "Charger suffers a fault and stops.\n");
+ else
+ dev_err(data->dev, "Charger recovered from a fault.\n");
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t max8903_chg(int irq, void *_data)
+{
+ struct max8903_data *data = _data;
+ struct max8903_pdata *pdata = data->pdata;
+ int chg_state;
+
+ chg_state = gpio_get_value(pdata->chg) ? false : true;
+
+ if (chg_state == data->chg_state)
+ return IRQ_HANDLED;
+
+ data->chg_state = chg_state;
+ return IRQ_HANDLED;
+}
+
+static void max8903_battery_work(struct work_struct *work)
+{
+ struct max8903_data *data;
+ data = container_of(work, struct max8903_data, work.work);
+ data->interval = HZ * BATTERY_UPDATE_INTERVAL;
+
+ max8903_charger_update_status(data);
+ max8903_battery_update_status(data);
+
+ pr_debug("battery voltage: %4d mV\n" , data->voltage_uV);
+ pr_debug("charger online status: %d\n" , data->charger_online);
+ pr_debug("battery status : %d\n" , data->battery_status);
+ pr_debug("battery capacity percent: %3d\n" , data->percent);
+ pr_debug("data->usb_in: %x , data->ta_in: %x \n" , data->usb_in, data->ta_in);
+ /* reschedule for the next time */
+ schedule_delayed_work(&data->work, data->interval);
+}
+static __devinit int max8903_probe(struct platform_device *pdev)
+{
+ struct max8903_data *data;
+ struct device *dev = &pdev->dev;
+ struct max8903_pdata *pdata = pdev->dev.platform_data;
+ int ret = 0;
+ int gpio = 0;
+ int ta_in = 0;
+ int usb_in = 0;
+ int retval;
+
+ data = kzalloc(sizeof(struct max8903_data), GFP_KERNEL);
+ if (data == NULL) {
+ dev_err(dev, "Cannot allocate memory.\n");
+ return -ENOMEM;
+ }
+ data->pdata = pdata;
+ data->dev = dev;
+
+ platform_set_drvdata(pdev, data);
+ capacity_changed_flag = false;
+ data->usb_in = 0;
+ data->ta_in = 0;
+
+ if (pdata->dc_valid == false && pdata->usb_valid == false) {
+ dev_err(dev, "No valid power sources.\n");
+ printk(KERN_INFO "No valid power sources.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ if (pdata->dc_valid) {
+ if (pdata->dok && gpio_is_valid(pdata->dok)) {
+ gpio = pdata->dok; /* PULL_UPed Interrupt */
+ /* set DOK gpio input */
+ ret = gpio_request(gpio, "max8903-DOK");
+ if (ret) {
+ printk(KERN_ERR"request max8903-DOK error!!\n");
+ goto err;
+ } else {
+ gpio_direction_input(gpio);
+ }
+ ta_in = gpio_get_value(gpio) ? 0 : 1;
+ } else if (pdata->dok && gpio_is_valid(pdata->dok) && pdata->dcm_always_high) {
+ ta_in = pdata->dok; /* PULL_UPed Interrupt */
+ ta_in = gpio_get_value(gpio) ? 0 : 1;
+ } else {
+ dev_err(dev, "When DC is wired, DOK and DCM should"
+ " be wired as well."
+ " or set dcm always high\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+ if (pdata->usb_valid) {
+ if (pdata->uok && gpio_is_valid(pdata->uok)) {
+ gpio = pdata->uok;
+ /* set UOK gpio input */
+ ret = gpio_request(gpio, "max8903-UOK");
+ if (ret) {
+ printk(KERN_ERR"request max8903-UOK error!!\n");
+ goto err;
+ } else {
+ gpio_direction_input(gpio);
+ }
+ usb_in = gpio_get_value(gpio) ? 0 : 1;
+ } else {
+ dev_err(dev, "When USB is wired, UOK should be wired."
+ "as well.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+ if (pdata->chg) {
+ if (!gpio_is_valid(pdata->chg)) {
+ dev_err(dev, "Invalid pin: chg.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ /* set CHG gpio input */
+ ret = gpio_request(pdata->chg, "max8903-CHG");
+ if (ret) {
+ printk(KERN_ERR"request max8903-CHG error!!\n");
+ goto err;
+ } else {
+ gpio_direction_input(pdata->chg);
+ }
+ }
+ if (pdata->flt) {
+ if (!gpio_is_valid(pdata->flt)) {
+ dev_err(dev, "Invalid pin: flt.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ /* set FLT gpio input */
+ ret = gpio_request(pdata->flt, "max8903-FLT");
+ if (ret) {
+ printk(KERN_ERR"request max8903-FLT error!!\n");
+ goto err;
+ } else {
+ gpio_direction_input(pdata->flt);
+ }
+ }
+ if (pdata->usus) {
+ if (!gpio_is_valid(pdata->usus)) {
+ dev_err(dev, "Invalid pin: usus.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+ mutex_init(&data->work_lock);
+ data->fault = false;
+ data->ta_in = ta_in;
+ data->usb_in = usb_in;
+ data->psy.name = "max8903-ac";
+ data->psy.type = POWER_SUPPLY_TYPE_MAINS;
+ data->psy.get_property = max8903_get_property;
+ data->psy.properties = max8903_charger_props;
+ data->psy.num_properties = ARRAY_SIZE(max8903_charger_props);
+ ret = power_supply_register(dev, &data->psy);
+ if (ret) {
+ dev_err(dev, "failed: power supply register.\n");
+ goto err_psy;
+ }
+ data->bat.name = "max8903-charger";
+ data->bat.type = POWER_SUPPLY_TYPE_BATTERY;
+ data->bat.properties = max8903_battery_props;
+ data->bat.num_properties = ARRAY_SIZE(max8903_battery_props);
+ data->bat.get_property = max8903_battery_get_property;
+ data->bat.use_for_apm = 1;
+ retval = power_supply_register(&pdev->dev, &data->bat);
+ if (retval) {
+ dev_err(data->dev, "failed to register battery\n");
+ goto battery_failed;
+ }
+ INIT_DELAYED_WORK(&data->work, max8903_battery_work);
+ schedule_delayed_work(&data->work, data->interval);
+ if (pdata->dc_valid) {
+ ret = request_threaded_irq(gpio_to_irq(pdata->dok),
+ NULL, max8903_dcin,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "MAX8903 DC IN", data);
+ if (ret) {
+ dev_err(dev, "Cannot request irq %d for DC (%d)\n",
+ gpio_to_irq(pdata->dok), ret);
+ goto err_usb_irq;
+ }
+ }
+
+ if (pdata->usb_valid) {
+ ret = request_threaded_irq(gpio_to_irq(pdata->uok),
+ NULL, max8903_usbin,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "MAX8903 USB IN", data);
+ if (ret) {
+ dev_err(dev, "Cannot request irq %d for USB (%d)\n",
+ gpio_to_irq(pdata->uok), ret);
+ goto err_dc_irq;
+ }
+ }
+
+ if (pdata->flt) {
+ ret = request_threaded_irq(gpio_to_irq(pdata->flt),
+ NULL, max8903_fault,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "MAX8903 Fault", data);
+ if (ret) {
+ dev_err(dev, "Cannot request irq %d for Fault (%d)\n",
+ gpio_to_irq(pdata->flt), ret);
+ goto err_flt_irq;
+ }
+ }
+
+ if (pdata->chg) {
+ ret = request_threaded_irq(gpio_to_irq(pdata->chg),
+ NULL, max8903_chg,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "MAX8903 Fault", data);
+ if (ret) {
+ dev_err(dev, "Cannot request irq %d for Fault (%d)\n",
+ gpio_to_irq(pdata->flt), ret);
+ goto err_chg_irq;
+ }
+ }
+ return 0;
+err_psy:
+ power_supply_unregister(&data->psy);
+battery_failed:
+ power_supply_unregister(&data->bat);
+err_usb_irq:
+ if (pdata->usb_valid)
+ free_irq(gpio_to_irq(pdata->uok), data);
+ cancel_delayed_work(&data->work);
+err_dc_irq:
+ if (pdata->dc_valid)
+ free_irq(gpio_to_irq(pdata->dok), data);
+ cancel_delayed_work(&data->work);
+err_flt_irq:
+ if (pdata->usb_valid)
+ free_irq(gpio_to_irq(pdata->uok), data);
+ cancel_delayed_work(&data->work);
+err_chg_irq:
+ if (pdata->dc_valid)
+ free_irq(gpio_to_irq(pdata->dok), data);
+ cancel_delayed_work(&data->work);
+err:
+ if (pdata->uok)
+ gpio_free(pdata->uok);
+ if (pdata->dok)
+ gpio_free(pdata->dok);
+ if (pdata->flt)
+ gpio_free(pdata->flt);
+ if (pdata->chg)
+ gpio_free(pdata->chg);
+ kfree(data);
+ return ret;
+}
+
+static __devexit int max8903_remove(struct platform_device *pdev)
+{
+ struct max8903_data *data = platform_get_drvdata(pdev);
+ if (data) {
+ struct max8903_pdata *pdata = data->pdata;
+ if (pdata->flt)
+ free_irq(gpio_to_irq(pdata->flt), data);
+ if (pdata->usb_valid)
+ free_irq(gpio_to_irq(pdata->uok), data);
+ if (pdata->dc_valid)
+ free_irq(gpio_to_irq(pdata->dok), data);
+ if (pdata->dc_valid)
+ free_irq(gpio_to_irq(pdata->chg), data);
+ cancel_delayed_work_sync(&data->work);
+ power_supply_unregister(&data->psy);
+ power_supply_unregister(&data->bat);
+ kfree(data);
+ }
+ return 0;
+}
+
+static int max8903_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct max8903_data *data = platform_get_drvdata(pdev);
+
+ cancel_delayed_work(&data->work);
+ return 0;
+}
+
+static int max8903_resume(struct platform_device *pdev)
+{
+ struct max8903_data *data = platform_get_drvdata(pdev);
+
+ schedule_delayed_work(&data->work, BATTERY_UPDATE_INTERVAL);
+ return 0;
+
+}
+
+static struct platform_driver max8903_driver = {
+ .probe = max8903_probe,
+ .remove = __devexit_p(max8903_remove),
+ .suspend = max8903_suspend,
+ .resume = max8903_resume,
+ .driver = {
+ .name = "max8903-charger",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init max8903_init(void)
+{
+ return platform_driver_register(&max8903_driver);
+}
+module_init(max8903_init);
+
+static void __exit max8903_exit(void)
+{
+ platform_driver_unregister(&max8903_driver);
+}
+module_exit(max8903_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Sabresd Battery Driver");
+MODULE_ALIAS("sabresd_battery");
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 0a6deb268b38..a79163f0ae40 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -276,7 +276,7 @@ static ssize_t regulator_uV_show(struct device *dev,
return ret;
}
static ssize_t regulator_uV_store(struct device *dev,
- struct device_attribute *attr, char *buf,
+ struct device_attribute *attr, const char *buf,
size_t count)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c
index 34ca0fb45961..e2aaa2d4e889 100644
--- a/drivers/regulator/pfuze100-regulator.c
+++ b/drivers/regulator/pfuze100-regulator.c
@@ -657,6 +657,21 @@ static int __devexit pfuze100_regulator_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_MX6_INTER_LDO_BYPASS
+int cpu_freq_suspend_in; /*judge for in suspend or not*/
+static int pfuze100_regulator_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ cpu_freq_suspend_in = 1;
+ return 0;
+}
+
+static int pfuze100_regulator_resume(struct platform_device *pdev)
+{
+ cpu_freq_suspend_in = 0;
+ return 0;
+}
+#endif
static struct platform_driver pfuze100_regulator_driver = {
.driver = {
.name = "pfuze100-regulator",
@@ -664,6 +679,10 @@ static struct platform_driver pfuze100_regulator_driver = {
},
.remove = __devexit_p(pfuze100_regulator_remove),
.probe = pfuze100_regulator_probe,
+#ifdef CONFIG_MX6_INTER_LDO_BYPASS
+ .suspend = pfuze100_regulator_suspend,
+ .resume = pfuze100_regulator_resume,
+#endif
};
static int __init pfuze100_regulator_init(void)
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 781e11f5cf34..beba18533371 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -215,6 +215,12 @@ struct imx_port {
wait_queue_head_t dma_wait;
};
+struct imx_port_ucrs {
+ unsigned int ucr1;
+ unsigned int ucr2;
+ unsigned int ucr3;
+};
+
#ifdef CONFIG_IRDA
#define USE_IRDA(sport) ((sport)->use_irda)
#else
@@ -222,6 +228,27 @@ struct imx_port {
#endif
/*
+ * Save and restore functions for UCR1, UCR2 and UCR3 registers
+ */
+static void imx_port_ucrs_save(struct uart_port *port,
+ struct imx_port_ucrs *ucr)
+{
+ /* save control registers */
+ ucr->ucr1 = readl(port->membase + UCR1);
+ ucr->ucr2 = readl(port->membase + UCR2);
+ ucr->ucr3 = readl(port->membase + UCR3);
+}
+
+static void imx_port_ucrs_restore(struct uart_port *port,
+ struct imx_port_ucrs *ucr)
+{
+ /* restore control registers */
+ writel(ucr->ucr1, port->membase + UCR1);
+ writel(ucr->ucr2, port->membase + UCR2);
+ writel(ucr->ucr3, port->membase + UCR3);
+}
+
+/*
* Handle any change of modem status signal since we were last called.
*/
static void imx_mctrl_check(struct imx_port *sport)
@@ -553,10 +580,8 @@ static irqreturn_t imx_rxint(int irq, void *dev_id)
continue;
}
- spin_unlock_irqrestore(&sport->port.lock, flags);
if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx))
continue;
- spin_lock_irqsave(&sport->port.lock, flags);
if (unlikely(rx & URXD_ERR)) {
if (rx & URXD_BRK)
@@ -1406,6 +1431,70 @@ imx_verify_port(struct uart_port *port, struct serial_struct *ser)
return ret;
}
+#if defined(CONFIG_CONSOLE_POLL)
+static int imx_poll_get_char(struct uart_port *port)
+{
+ struct imx_port_ucrs old_ucr;
+ unsigned int status;
+ unsigned char c;
+
+ /* save control registers */
+ imx_port_ucrs_save(port, &old_ucr);
+
+ /* disable interrupts */
+ writel(UCR1_UARTEN, port->membase + UCR1);
+ writel(old_ucr.ucr2 & ~(UCR2_ATEN | UCR2_RTSEN | UCR2_ESCI),
+ port->membase + UCR2);
+ writel(old_ucr.ucr3 & ~(UCR3_DCD | UCR3_RI | UCR3_DTREN),
+ port->membase + UCR3);
+
+ /* poll */
+ do {
+ status = readl(port->membase + USR2);
+ } while (~status & USR2_RDR);
+
+ /* read */
+ c = readl(port->membase + URXD0);
+
+ /* restore control registers */
+ imx_port_ucrs_restore(port, &old_ucr);
+
+ return c;
+}
+
+static void imx_poll_put_char(struct uart_port *port, unsigned char c)
+{
+ struct imx_port_ucrs old_ucr;
+ unsigned int status;
+
+ /* save control registers */
+ imx_port_ucrs_save(port, &old_ucr);
+
+ /* disable interrupts */
+ writel(UCR1_UARTEN, port->membase + UCR1);
+ writel(old_ucr.ucr2 & ~(UCR2_ATEN | UCR2_RTSEN | UCR2_ESCI),
+ port->membase + UCR2);
+ writel(old_ucr.ucr3 & ~(UCR3_DCD | UCR3_RI | UCR3_DTREN),
+ port->membase + UCR3);
+
+ /* drain */
+ do {
+ status = readl(port->membase + USR1);
+ } while (~status & USR1_TRDY);
+
+ /* write */
+ writel(c, port->membase + URTX0);
+
+ /* flush */
+ do {
+ status = readl(port->membase + USR2);
+ } while (~status & USR2_TXDC);
+
+ /* restore control registers */
+ imx_port_ucrs_restore(port, &old_ucr);
+}
+#endif
+
static struct uart_ops imx_pops = {
.tx_empty = imx_tx_empty,
.set_mctrl = imx_set_mctrl,
@@ -1423,6 +1512,10 @@ static struct uart_ops imx_pops = {
.request_port = imx_request_port,
.config_port = imx_config_port,
.verify_port = imx_verify_port,
+#if defined(CONFIG_CONSOLE_POLL)
+ .poll_get_char = imx_poll_get_char,
+ .poll_put_char = imx_poll_put_char,
+#endif
};
static struct imx_port *imx_ports[UART_NR];
@@ -1445,15 +1538,22 @@ static void
imx_console_write(struct console *co, const char *s, unsigned int count)
{
struct imx_port *sport = imx_ports[co->index];
- unsigned int old_ucr1, old_ucr2, ucr1;
+ struct imx_port_ucrs old_ucr;
+ unsigned int ucr1;
unsigned long flags;
+ int locked = 1;
+
+ local_irq_save(flags);
+ if (sport->port.sysrq)
+ locked = 0;
+ else
+ spin_lock(&sport->port.lock);
- spin_lock_irqsave(&sport->port.lock, flags);
/*
- * First, save UCR1/2 and then disable interrupts
+ * First, save UCR1/2/3 and then disable interrupts
*/
- ucr1 = old_ucr1 = readl(sport->port.membase + UCR1);
- old_ucr2 = readl(sport->port.membase + UCR2);
+ imx_port_ucrs_save(&sport->port, &old_ucr);
+ ucr1 = old_ucr.ucr1;
if (cpu_is_mx1())
ucr1 |= MX1_UCR1_UARTCLKEN;
@@ -1462,19 +1562,21 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
writel(ucr1, sport->port.membase + UCR1);
- writel(old_ucr2 | UCR2_TXEN, sport->port.membase + UCR2);
+ writel(old_ucr.ucr2 | UCR2_TXEN, sport->port.membase + UCR2);
uart_console_write(&sport->port, s, count, imx_console_putchar);
/*
* Finally, wait for transmitter to become empty
- * and restore UCR1/2
+ * and restore UCR1/2/3
*/
while (!(readl(sport->port.membase + USR2) & USR2_TXDC));
- writel(old_ucr1, sport->port.membase + UCR1);
- writel(old_ucr2, sport->port.membase + UCR2);
- spin_unlock_irqrestore(&sport->port.lock, flags);
+ imx_port_ucrs_restore(&sport->port, &old_ucr);
+
+ if (locked)
+ spin_unlock(&sport->port.lock);
+ local_irq_restore(flags);
}
/*
diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c
index 5440b0eb71ac..c6e5d054b8bc 100644
--- a/drivers/usb/gadget/zero.c
+++ b/drivers/usb/gadget/zero.c
@@ -107,16 +107,26 @@ module_param(loopdefault, bool, S_IRUGO|S_IWUSR);
#else
#define DRIVER_VENDOR_NUM 0x1a0a /* OTG test device IDs */
#define DRIVER_PRODUCT_NUM 0xbadd
-#define DEFAULT_AUTORESUME 5
+#define DEFAULT_AUTORESUME 5000
#endif
+static unsigned startms;
+
/* If the optional "autoresume" mode is enabled, it provides good
* functional coverage for the "USBCV" test harness from USB-IF.
* It's always set if OTG mode is enabled.
*/
-unsigned autoresume = DEFAULT_AUTORESUME;
+static unsigned autoresume = DEFAULT_AUTORESUME;
module_param(autoresume, uint, S_IRUGO);
-MODULE_PARM_DESC(autoresume, "zero, or seconds before remote wakeup");
+MODULE_PARM_DESC(autoresume, "minimum milliseconds before sending remote wakeup");
+
+static unsigned interval;
+module_param(interval, uint, S_IRUGO);
+MODULE_PARM_DESC(interval, "zero, or milliseconds for interval remote wakeup time");
+
+static unsigned endms = 5000;
+module_param(endms, uint, S_IRUGO);
+MODULE_PARM_DESC(endms, "max milliseconds before sending remote wakeup");
/*-------------------------------------------------------------------------*/
@@ -250,8 +260,11 @@ static void zero_suspend(struct usb_composite_dev *cdev)
return;
if (autoresume) {
- mod_timer(&autoresume_timer, jiffies + (HZ * autoresume));
- DBG(cdev, "suspend, wakeup in %d seconds\n", autoresume);
+ mod_timer(&autoresume_timer, jiffies + msecs_to_jiffies(startms));
+ DBG(cdev, "suspend, wakeup in %d milliseconds\n", startms);
+ startms += interval;
+ if (startms == endms)
+ startms = autoresume;
} else
DBG(cdev, "%s\n", __func__);
}
@@ -327,6 +340,10 @@ static int __ref zero_bind(struct usb_composite_dev *cdev)
init_utsname()->sysname, init_utsname()->release,
gadget->name);
+ startms = autoresume;
+ if (autoresume > 0 && autoresume < 5)
+ pr_warning("%s: time before sending remote wakeup is less than 5ms, should not send resume signal.\n",
+ longname);
return 0;
}
@@ -336,6 +353,11 @@ static int zero_unbind(struct usb_composite_dev *cdev)
return 0;
}
+static void zero_disconnect(struct usb_composite_dev *cdev)
+{
+ startms = autoresume;
+}
+
static struct usb_composite_driver zero_driver = {
.name = "zero",
.dev = &device_desc,
@@ -343,6 +365,7 @@ static struct usb_composite_driver zero_driver = {
.unbind = zero_unbind,
.suspend = zero_suspend,
.resume = zero_resume,
+ .disconnect = zero_disconnect,
};
MODULE_AUTHOR("David Brownell");
diff --git a/drivers/video/mxc/Kconfig b/drivers/video/mxc/Kconfig
index fe9de7362544..f0afddc95bd3 100644
--- a/drivers/video/mxc/Kconfig
+++ b/drivers/video/mxc/Kconfig
@@ -104,6 +104,17 @@ config FB_MXC_EINK_AUTO_UPDATE_MODE
default n
depends on FB_MXC_EINK_PANEL
+config FB_MXC_SIPIX_PANEL
+ depends on FB_MXC
+ depends on DMA_ENGINE
+ select FB_DEFERRED_IO
+ tristate "SIPIX Panel Framebuffer"
+
+config FB_MXC_SIPIX_AUTO_UPDATE_MODE
+ bool "SIPIX Auto-update Mode Support"
+ default n
+ depends on FB_MXC_SIPIX_PANEL
+
config FB_MXC_ELCDIF_FB
depends on FB && ARCH_MXC
tristate "Support MXC ELCDIF framebuffer"
diff --git a/drivers/video/mxc/Makefile b/drivers/video/mxc/Makefile
index 084837c70929..21aef052ecd6 100644
--- a/drivers/video/mxc/Makefile
+++ b/drivers/video/mxc/Makefile
@@ -25,4 +25,5 @@ obj-$(CONFIG_FB_MXC_SEIKO_WVGA_SYNC_PANEL) += mxcfb_seiko_wvga.o
obj-$(CONFIG_FB_MXC_TVOUT_CH7024) += ch7024.o
obj-$(CONFIG_FB_MXC_CH7026) += mxcfb_ch7026.o
obj-$(CONFIG_FB_MXC_EINK_PANEL) += mxc_epdc_fb.o
+obj-$(CONFIG_FB_MXC_SIPIX_PANEL) += mxc_spdc_fb.o
obj-$(CONFIG_FB_MXC_ELCDIF_FB) += mxc_elcdif_fb.o
diff --git a/drivers/video/mxc/epdc_regs.h b/drivers/video/mxc/epdc_regs.h
index 50d352e7a212..cc7b5000db18 100644
--- a/drivers/video/mxc/epdc_regs.h
+++ b/drivers/video/mxc/epdc_regs.h
@@ -163,9 +163,7 @@ extern void __iomem *epdc_base;
#define EPDC_PIGEON_15_0 (epdc_base + 0xBC0)
#define EPDC_PIGEON_15_1 (epdc_base + 0xBD0)
#define EPDC_PIGEON_15_2 (epdc_base + 0xBE0)
-#define EPDC_PIGEON_16_0 (epdc_base + 0xC00)
-#define EPDC_PIGEON_16_1 (epdc_base + 0xC10)
-#define EPDC_PIGEON_16_2 (epdc_base + 0xC20)
+#define EPDC_WB_ADDR_TCE (epdc_base + 0xC10)
/*
* Register field definitions
diff --git a/drivers/video/mxc/ldb.c b/drivers/video/mxc/ldb.c
index 493abd662036..8b84498fd285 100644
--- a/drivers/video/mxc/ldb.c
+++ b/drivers/video/mxc/ldb.c
@@ -457,6 +457,17 @@ static int ldb_disp_init(struct mxc_dispdrv_handle *disp,
else
ldb->mode = plat_data->mode;
+ if ((ldb->mode == LDB_SIN0) || (ldb->mode == LDB_SIN1)) {
+ ret = ldb->mode - LDB_SIN0;
+ if (plat_data->disp_id != ret) {
+ dev_warn(&ldb->pdev->dev,
+ "change IPU DI%d to IPU DI%d for LDB "
+ "channel%d.\n",
+ plat_data->disp_id, ret, ret);
+ plat_data->disp_id = ret;
+ }
+ }
+
if (ldb->mode == LDB_SPL_DI0) {
reg |= LDB_SPLIT_MODE_EN | LDB_CH0_MODE_EN_TO_DI0
| LDB_CH1_MODE_EN_TO_DI0;
diff --git a/drivers/video/mxc/mxc_edid.c b/drivers/video/mxc/mxc_edid.c
index e8a2b9761b65..f86313df7dea 100644
--- a/drivers/video/mxc/mxc_edid.c
+++ b/drivers/video/mxc/mxc_edid.c
@@ -350,6 +350,16 @@ int mxc_edid_parse_ext_blk(unsigned char *edid,
break;
}
case 0x4: /*Speaker allocation block*/
+ {
+ i = 0;
+ while (i < blklen) {
+ cfg->speaker_alloc = edid[index + 1];
+ index += 3;
+ i += 3;
+ DPRINTK("Speaker Alloc : %02x\n", cfg->speaker_alloc);
+ }
+ break;
+ }
case 0x7: /*User extended block*/
default:
/* skip */
diff --git a/drivers/video/mxc/mxc_elcdif_fb.c b/drivers/video/mxc/mxc_elcdif_fb.c
index 7a5fa6d1900c..2c96eecc111f 100644
--- a/drivers/video/mxc/mxc_elcdif_fb.c
+++ b/drivers/video/mxc/mxc_elcdif_fb.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2010-2012 Freescale Semiconductor, Inc.
*/
/*
@@ -128,8 +128,10 @@ static inline void setup_dotclk_panel(u32 pixel_clk,
__raw_writel(BM_ELCDIF_CTRL_SHIFT_NUM_BITS,
elcdif_base + HW_ELCDIF_CTRL_CLR);
+ __raw_writel(BM_ELCDIF_CTRL2_OUTSTANDING_REQS,
+ elcdif_base + HW_ELCDIF_CTRL2_CLR);
__raw_writel(BF_ELCDIF_CTRL2_OUTSTANDING_REQS
- (BV_ELCDIF_CTRL2_OUTSTANDING_REQS__REQ_8),
+ (BV_ELCDIF_CTRL2_OUTSTANDING_REQS__REQ_16),
elcdif_base + HW_ELCDIF_CTRL2_SET);
/* Recover on underflow */
@@ -824,7 +826,7 @@ static int mxc_elcdif_fb_set_par(struct fb_info *fbi)
mxc_init_elcdif();
mxc_elcdif_init_panel();
- dev_dbg(fbi->device, "pixclock = %ul Hz\n",
+ dev_dbg(fbi->device, "pixclock = %lu Hz\n",
(u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL));
memset(&sig_cfg, 0, sizeof(sig_cfg));
@@ -1369,7 +1371,7 @@ static int mxc_elcdif_fb_suspend(struct platform_device *pdev,
struct mxc_elcdif_fb_data *data = (struct mxc_elcdif_fb_data *)fbi->par;
int saved_blank;
- acquire_console_sem();
+ console_lock();
fb_set_suspend(fbi, 1);
saved_blank = data->cur_blank;
mxc_elcdif_fb_blank(FB_BLANK_POWERDOWN, fbi);
@@ -1388,7 +1390,7 @@ static int mxc_elcdif_fb_suspend(struct platform_device *pdev,
clk_disable(g_elcdif_axi_clk);
g_elcdif_axi_clk_enable = false;
}
- release_console_sem();
+ console_unlock();
return 0;
}
@@ -1397,10 +1399,10 @@ static int mxc_elcdif_fb_resume(struct platform_device *pdev)
struct fb_info *fbi = platform_get_drvdata(pdev);
struct mxc_elcdif_fb_data *data = (struct mxc_elcdif_fb_data *)fbi->par;
- acquire_console_sem();
+ console_lock();
mxc_elcdif_fb_blank(data->next_blank, fbi);
fb_set_suspend(fbi, 0);
- release_console_sem();
+ console_unlock();
return 0;
}
diff --git a/drivers/video/mxc/mxc_epdc_fb.c b/drivers/video/mxc/mxc_epdc_fb.c
index da77373e03a6..ef4916db0436 100644
--- a/drivers/video/mxc/mxc_epdc_fb.c
+++ b/drivers/video/mxc/mxc_epdc_fb.c
@@ -1130,6 +1130,7 @@ static void epdc_init_sequence(struct mxc_epdc_fb_data *fb_data)
epdc_init_settings(fb_data);
__raw_writel(fb_data->waveform_buffer_phys, EPDC_WVADDR);
__raw_writel(fb_data->working_buffer_phys, EPDC_WB_ADDR);
+ __raw_writel(fb_data->working_buffer_phys, EPDC_WB_ADDR_TCE);
fb_data->in_init = true;
epdc_powerup(fb_data);
draw_mode0(fb_data);
@@ -4006,8 +4007,8 @@ static void mxc_epdc_fb_fw_handler(const struct firmware *fw,
clk_enable(fb_data->epdc_clk_pix);
rounded_pix_clk = clk_round_rate(fb_data->epdc_clk_pix, target_pix_clk);
- if (((rounded_pix_clk >= target_pix_clk + target_pix_clk/200) ||
- (rounded_pix_clk <= target_pix_clk - target_pix_clk/200))) {
+ if (((rounded_pix_clk >= target_pix_clk + target_pix_clk/100) ||
+ (rounded_pix_clk <= target_pix_clk - target_pix_clk/100))) {
/* Can't get close enough without changing parent clk */
epdc_parent = clk_get_parent(fb_data->epdc_clk_pix);
rounded_parent_rate = clk_round_rate(epdc_parent, target_pix_clk);
@@ -4018,8 +4019,8 @@ static void mxc_epdc_fb_fw_handler(const struct firmware *fw,
clk_set_rate(epdc_parent, epdc_pix_rate);
rounded_pix_clk = clk_round_rate(fb_data->epdc_clk_pix, target_pix_clk);
- if (((rounded_pix_clk >= target_pix_clk + target_pix_clk/200) ||
- (rounded_pix_clk <= target_pix_clk - target_pix_clk/200)))
+ if (((rounded_pix_clk >= target_pix_clk + target_pix_clk/100) ||
+ (rounded_pix_clk <= target_pix_clk - target_pix_clk/100)))
/* Still can't get a good clock, provide warning */
dev_err(fb_data->dev, "Unable to get an accurate EPDC pix clk"
"desired = %lu, actual = %lu\n", target_pix_clk,
diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c
index 11bccff413a8..348bd818ee69 100644
--- a/drivers/video/mxc/mxc_ipuv3_fb.c
+++ b/drivers/video/mxc/mxc_ipuv3_fb.c
@@ -316,15 +316,6 @@ static int mxcfb_set_par(struct fb_info *fbi)
dev_dbg(fbi->device, "Reconfiguring framebuffer\n");
- if (mxc_fbi->dispdrv && mxc_fbi->dispdrv->drv->setup) {
- retval = mxc_fbi->dispdrv->drv->setup(mxc_fbi->dispdrv, fbi);
- if (retval < 0) {
- dev_err(fbi->device, "setup error, dispdrv:%s.\n",
- mxc_fbi->dispdrv->drv->name);
- return -EINVAL;
- }
- }
-
ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq);
ipu_disable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq);
ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq);
@@ -391,6 +382,15 @@ static int mxcfb_set_par(struct fb_info *fbi)
if (mxc_fbi->next_blank != FB_BLANK_UNBLANK)
return retval;
+ if (mxc_fbi->dispdrv && mxc_fbi->dispdrv->drv->setup) {
+ retval = mxc_fbi->dispdrv->drv->setup(mxc_fbi->dispdrv, fbi);
+ if (retval < 0) {
+ dev_err(fbi->device, "setup error, dispdrv:%s.\n",
+ mxc_fbi->dispdrv->drv->name);
+ return -EINVAL;
+ }
+ }
+
_setup_disp_channel1(fbi);
if (!mxc_fbi->overlay) {
diff --git a/drivers/video/mxc/mxc_spdc_fb.c b/drivers/video/mxc/mxc_spdc_fb.c
new file mode 100644
index 000000000000..37938b9db712
--- /dev/null
+++ b/drivers/video/mxc/mxc_spdc_fb.c
@@ -0,0 +1,4168 @@
+/*
+ * Copyright (C) 2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+/*
+ * Based on MXC EPDC Driver, Freescale Solutions, Inc All Rights Reserved.
+ */
+
+#include "mxc_spdc_fb.h"
+
+#define MERGE_OK 0
+#define MERGE_FAIL 1
+#define MERGE_BLOCK 2
+
+#define SPDC_DEFAULT_TEMP 30
+#define TEMP_NO_SET 0xFF
+#define POWER_STATE_OFF 0
+#define POWER_STATE_ON 1
+#define POWER_READY_OFF false
+#define POWER_READY_ON true
+
+#define INIT_UPDATE_MARKER 0x12345678
+#define PAN_UPDATE_MARKER 0x12345679
+
+#define SPDC_MAX_NUM_UPDATES 32
+#define SPDC_MAX_NUM_BUFFERS 2
+#define SPDC_MAX_NUM_PREPROCESS 15
+#define NUM_SCREENS_MIN 2
+#define SPDC_DEFAULT_BPP 16
+
+mxc_spdc_t *g_fb_data;
+
+static int mxc_spdc_fb_send_update(struct mxcfb_update_data *upd_data,
+ struct fb_info *info);
+static int mxc_spdc_fb_wait_update_complete(struct mxcfb_update_marker_data
+ *marker_data, struct fb_info *info);
+
+static const struct mxc_spdc_resolution_map_para spdc_gray_res_map[] = {
+/*define Gray Mode resolution mapping*/
+ {0x18, 600, 800, PORTRAIT},
+ {0x19, 768, 1024, PORTRAIT},
+ {0x1a, 0, 0, RESERVED},
+ {0x1b, 600, 1024, PORTRAIT},
+ {0x1c, 825, 1200, PORTRAIT},
+ {0x1d, 1024, 1280, PORTRAIT},
+ {0x1e, 1200, 1600, PORTRAIT},
+ {0x10, 800, 1024, PORTRAIT},
+ {0x11, 825, 1280, PORTRAIT},
+ {0x12, 800, 1280, PORTRAIT},
+ {0x13, 768, 1280, PORTRAIT},
+ {0x14, 960, 1280, PORTRAIT},
+ {0x0, 800, 600, LANDSCAPE},
+ {0x1, 1024, 768, LANDSCAPE},
+ {0x2, 0, 0, RESERVED},
+ {0x3, 1024, 600, LANDSCAPE},
+ {0x4, 1200, 825, LANDSCAPE},
+ {0x5, 1280, 1024, LANDSCAPE},
+ {0x6, 1600, 1200, LANDSCAPE},
+ {0x7, 1024, 800, LANDSCAPE},
+ {0x8, 1280, 825, LANDSCAPE},
+ {0x9, 1280, 800, LANDSCAPE},
+ {0xa, 1280, 768, LANDSCAPE},
+ {0xb, 1280, 960, LANDSCAPE},
+ {0xFFFF, 800, 600, LANDSCAPE},
+};
+
+static const struct mxc_spdc_resolution_map_para spdc_rgbw_res_map[] = {
+/*define RGBW Mode resolution mapping*/
+ {0x18, 300, 400, PORTRAIT},
+ {0x19, 384, 512, PORTRAIT},
+ {0x1a, 0, 0, RESERVED},
+ {0x1b, 300, 512, PORTRAIT},
+ {0x1c, 0, 0, RESERVED},
+ {0x1d, 512, 640, PORTRAIT},
+ {0x1e, 600, 800, PORTRAIT},
+ {0x10, 400, 512, PORTRAIT},
+ {0x11, 0, 0, RESERVED},
+ {0x12, 400, 640, PORTRAIT},
+ {0x13, 384, 640, PORTRAIT},
+ {0x14, 480, 640, PORTRAIT},
+ {0x0, 400, 300, LANDSCAPE},
+ {0x1, 512, 384, LANDSCAPE},
+ {0x2, 0, 0, RESERVED},
+ {0x3, 512, 300, LANDSCAPE},
+ {0x4, 0, 0, RESERVED},
+ {0x5, 640, 512, LANDSCAPE},
+ {0x6, 800, 600, LANDSCAPE},
+ {0x7, 512, 400, LANDSCAPE},
+ {0x8, 0, 0, RESERVED},
+ {0x9, 640, 400, LANDSCAPE},
+ {0xa, 640, 384, LANDSCAPE},
+ {0xb, 640, 480, LANDSCAPE},
+ {0xFFFF, 400, 300, LANDSCAPE},
+};
+
+static void get_panel_init_set(struct imx_spdc_panel_init_set*
+ panel_set, u32 *val)
+{
+ *val = panel_set->yoe_pol |
+ (panel_set->dual_gate << 1) |
+ (panel_set->ud << 7) |
+ (panel_set->rl << 8) |
+ (panel_set->data_filter_n << 9) |
+ (panel_set->power_ready << 10) |
+ (panel_set->rgbw_mode_enable << 11) |
+ (panel_set->hburst_len_en << 13) |
+ ((panel_set->resolution & 0x1F) << 2);
+}
+
+static inline void spdc_intr_enable(mxc_spdc_t *fb_data, u32 int_type)
+{
+ u32 status;
+
+ status = __raw_readl(fb_data->hwp + SPDC_INT_ENABLE);
+ status |= (int_type & SPDC_IRQ_ALL_MASK);
+ __raw_writel(status, fb_data->hwp + SPDC_INT_ENABLE);
+}
+
+static bool spdc_is_update_finish(mxc_spdc_t *fb_data)
+{
+ u32 val = __raw_readl(fb_data->hwp + SPDC_INT_STA_CLR);
+ bool is_finish = (val & SPDC_IRQ_STA_FRAME_UPDATE) ? true : false;
+
+ return is_finish;
+}
+
+static int spdc_get_intr_stat(mxc_spdc_t *fb_data)
+{
+ u32 status = __raw_readl(fb_data->hwp + SPDC_INT_STA_CLR);
+ return status & 0xF;
+}
+
+static inline void
+spdc_intr_stat_clear(mxc_spdc_t *fb_data, u32 int_type)
+{
+ /* write 1 to clear status */
+ u32 status = (int_type & SPDC_IRQ_STA_ALL_MASK);
+ __raw_writel(status, fb_data->hwp + SPDC_INT_STA_CLR);
+}
+
+static inline void spdc_set_nextbuf_addr(mxc_spdc_t *fb_data)
+{
+ u32 addr = fb_data->fresh_param.buf_addr.next_buf_phys_addr;
+ __raw_writel(addr, fb_data->hwp + SPDC_NEXT_BUF);
+ dev_dbg(fb_data->dev, "add: 0x%x\n", addr);
+}
+
+static inline void spdc_set_curbuf_addr(mxc_spdc_t *fb_data)
+{
+ u32 addr = fb_data->fresh_param.buf_addr.cur_buf_phys_addr;
+ __raw_writel(addr, fb_data->hwp + SPDC_CURRENT_BUF);
+}
+
+static inline void spdc_set_prebuf_addr(mxc_spdc_t *fb_data)
+{
+ u32 addr = fb_data->fresh_param.buf_addr.pre_buf_phys_addr;
+ __raw_writel(addr, fb_data->hwp + SPDC_PRE_BUF);
+}
+
+static inline void spdc_set_cntbuf_addr(mxc_spdc_t *fb_data)
+{
+ u32 addr = fb_data->fresh_param.buf_addr.frm_cnt_buf_phys_addr;
+ __raw_writel(addr, fb_data->hwp + SPDC_CNT_BUF);
+}
+
+static inline void spdc_set_lutbuf_addr(mxc_spdc_t *fb_data)
+{
+ u32 addr = fb_data->fresh_param.buf_addr.lut_buf_phys_addr;
+ __raw_writel(addr, fb_data->hwp + SPDC_LUT_BUF);
+}
+
+static inline void spdc_set_update_coord(mxc_spdc_t *fb_data)
+{
+ u32 x = fb_data->fresh_param.update_region.left;
+ u32 y = fb_data->fresh_param.update_region.top;
+
+ if (!x)
+ x++;
+ if (!y)
+ y++;
+
+ dev_dbg(fb_data->dev, "x:%d, y:%d\n", x, y);
+ x = (u32)(((x & SPDC_UPDATE_X_Y_MAX_SIZE) << 16) |
+ (y & SPDC_UPDATE_X_Y_MAX_SIZE));
+ __raw_writel(x, fb_data->hwp + SPDC_UPDATA_X_Y);
+}
+
+static inline void spdc_set_update_dimensions(mxc_spdc_t *fb_data)
+{
+ u32 w = fb_data->fresh_param.update_region.width;
+ u32 h = fb_data->fresh_param.update_region.height;
+
+ if (!w)
+ w++;
+ if (!h)
+ h++;
+
+ dev_dbg(fb_data->dev, "w:%d, h:%d\n", w, h);
+ w = (u32)(((w & SPDC_UPDATE_W_H_MAX_SIZE) << 16) |
+ (h & SPDC_UPDATE_W_H_MAX_SIZE));
+ __raw_writel(w, fb_data->hwp + SPDC_UPDATE_W_H);
+}
+
+static inline void spdc_set_update_temper(mxc_spdc_t *fb_data)
+{
+ s8 temper = (s8)(fb_data->fresh_param.temper & 0xFF) << 1;
+
+ if (temper > -110 && temper < 200)
+ __raw_writel(temper, fb_data->hwp + SPDC_TEMP_INFO);
+ else
+ __raw_writel(SPDC_DEFAULT_TEMP, fb_data->hwp + SPDC_TEMP_INFO);
+}
+
+static inline void spdc_trigger_update(mxc_spdc_t *fb_data)
+{
+ u32 val;
+ struct partial_refresh_param *fresh_param = &fb_data->fresh_param;
+
+ if ((fresh_param->wave_mode & SPDC_WAV_MODE_MASK) && fresh_param->flash)
+ val = SPDC_DISP_TRIGGER_FLASH;
+ else
+ val = 0;
+
+ val |= fresh_param->wave_mode << 1;
+ val |= SPDC_DISP_TRIGGER_ENABLE;
+ dev_dbg(fb_data->dev, "wave:%d\n", fresh_param->wave_mode);
+ __raw_writel(val, fb_data->hwp + SPDC_DISP_TRIGGER);
+}
+
+static bool is_lut_checksum_ok(mxc_spdc_t *fb_data)
+{
+ u32 status;
+
+ status = __raw_readl(fb_data->hwp + SPDC_STATUS);
+ status &= SPDC_IRQ_STA_ERR;
+
+ return status ? true : false;
+}
+
+static void spdc_clk_gate(mxc_spdc_t *fb_data, bool enable)
+{
+ if (enable)
+ __raw_writel(SPDC_SW_GATE_CLK_ENABLE,
+ fb_data->hwp + SPDC_SW_GATE_CLK);
+ else
+ __raw_writel(~SPDC_SW_GATE_CLK_ENABLE,
+ fb_data->hwp + SPDC_SW_GATE_CLK);
+}
+
+static int update_panel_init_set(mxc_spdc_t *fb_data)
+{
+ int ret = 0;
+ u32 init_val;
+
+ get_panel_init_set(&fb_data->panel_set, &init_val);
+ dev_dbg(fb_data->dev, "panel init setting:%x\n", init_val);
+
+ __raw_writel(init_val, fb_data->hwp + SPDC_PANEL_INIT_SET);
+
+ /*wait init setting update finish*/
+ ret = wait_for_completion_timeout(&fb_data->init_finish,
+ msecs_to_jiffies(4000));
+ if (!ret)
+ dev_err(fb_data->dev, "Timed out for init setting!\n");
+
+ return ret;
+}
+
+static void spdc_panel_pwr_on(mxc_spdc_t *fb_data)
+{
+ fb_data->panel_set.power_ready = POWER_READY_ON;
+}
+
+static void spdc_panel_pwr_down(mxc_spdc_t *fb_data)
+{
+ fb_data->panel_set.power_ready = POWER_READY_OFF;
+}
+
+static void spdc_powerdown(mxc_spdc_t *fb_data)
+{
+ mutex_lock(&fb_data->power_mutex);
+
+ /* If powering_down has been cleared, a powerup
+ * request is pre-empting this powerdown request.
+ */
+ if (!fb_data->powering_down
+ || (fb_data->power_state == POWER_STATE_OFF)) {
+ mutex_unlock(&fb_data->power_mutex);
+ return;
+ }
+
+ dev_dbg(fb_data->dev, "spdc Powerdown\n");
+
+ /* Disable power to the AUO panel */
+ regulator_disable(fb_data->vcom_regulator);
+ regulator_disable(fb_data->display_regulator);
+
+ /*enable spdc clock gating*/
+ spdc_clk_gate(fb_data, true);
+ clk_disable(fb_data->spdc_clk_pix);
+ clk_disable(fb_data->spdc_clk_axi);
+
+ /* Disable pins used by SPDC (to prevent leakage current) */
+ if (fb_data->pdata->disable_pins)
+ fb_data->pdata->disable_pins();
+
+ /* turn off the V3p3 */
+ regulator_disable(fb_data->v3p3_regulator);
+
+ fb_data->power_state = POWER_STATE_OFF;
+ fb_data->powering_down = false;
+ spdc_panel_pwr_down(fb_data);
+
+ if (fb_data->wait_for_powerdown) {
+ fb_data->wait_for_powerdown = false;
+ complete(&fb_data->powerdown_compl);
+ }
+
+ mutex_unlock(&fb_data->power_mutex);
+}
+
+static void spdc_powerup(mxc_spdc_t *fb_data)
+{
+ int ret = 0;
+ mutex_lock(&fb_data->power_mutex);
+
+ /*
+ * If power down request is pending, clear
+ * powering_down to cancel the request.
+ */
+ if (fb_data->powering_down)
+ fb_data->powering_down = false;
+
+ if (fb_data->power_state == POWER_STATE_ON) {
+ mutex_unlock(&fb_data->power_mutex);
+ return;
+ }
+
+ dev_dbg(fb_data->dev, "spdc Powerup\n");
+
+ /* Enable the v3p3 regulator */
+ ret = regulator_enable(fb_data->v3p3_regulator);
+ if (IS_ERR((void *)ret)) {
+ dev_err(fb_data->dev, "Unable to enable V3P3 regulator."
+ "err = 0x%x\n", ret);
+ mutex_unlock(&fb_data->power_mutex);
+ return;
+ }
+
+ msleep(1);
+
+ /* Enable pins used by SPDC */
+ if (fb_data->pdata->enable_pins)
+ fb_data->pdata->enable_pins();
+
+ /* Enable clocks to SPDC */
+ clk_enable(fb_data->spdc_clk_axi);
+ clk_enable(fb_data->spdc_clk_pix);
+
+ /*disable spdc gate*/
+ spdc_clk_gate(fb_data, false);
+
+ /* Enable power to the EPD panel */
+ ret = regulator_enable(fb_data->display_regulator);
+ if (IS_ERR((void *)ret)) {
+ dev_err(fb_data->dev, "Unable to enable DISPLAY regulator."
+ "err = 0x%x\n", ret);
+ mutex_unlock(&fb_data->power_mutex);
+ return;
+ }
+ ret = regulator_enable(fb_data->vcom_regulator);
+ if (IS_ERR((void *)ret)) {
+ dev_err(fb_data->dev, "Unable to enable VCOM regulator."
+ "err = 0x%x\n", ret);
+ mutex_unlock(&fb_data->power_mutex);
+ return;
+ }
+
+ fb_data->power_state = POWER_STATE_ON;
+ spdc_panel_pwr_on(fb_data);
+
+ mutex_unlock(&fb_data->power_mutex);
+}
+
+#ifdef DEBUG
+static void
+check_waveform(u32 *wv_buf_orig, u32 *wv_buf_cur, u32 wv_buf_size)
+{
+ int i;
+ bool is_mismatch = false;
+ for (i = 0; i < wv_buf_size; i++) {
+ if (wv_buf_orig[i] != wv_buf_cur[i]) {
+ is_mismatch = true;
+ printk(KERN_ERR "Waveform mismatch!\n");
+ }
+ }
+
+ if (!is_mismatch)
+ printk(KERN_DEBUG "No mismatches!\n");
+}
+#else
+static void
+check_waveform(u32 *wv_buf_orig, u32 *wv_buf_cur, u32 wv_buf_size) {}
+#endif
+
+static void get_spdc_version(mxc_spdc_t *fb_data)
+{
+ struct mxc_spdc_version *spdc_ver = &fb_data->spdc_ver;
+ u32 disp_id, tcon_id;
+
+ disp_id = __raw_readl(fb_data->hwp + SPDC_DISP_VER);
+ tcon_id = __raw_readl(fb_data->hwp + SPDC_TCON_VER);
+
+ spdc_ver->disp_ver.product_id = disp_id & 0xFFFF;
+ spdc_ver->disp_ver.lut_ver = (disp_id >> 16) & 0xFF;
+ spdc_ver->disp_ver.epd_type = (disp_id >> 24) & 0xFF;
+ spdc_ver->tcon_ver = tcon_id & 0xFF;
+
+ dev_info(fb_data->dev, "EPD type ID:%x, Tcon ID:%x\n",
+ spdc_ver->disp_ver.product_id, spdc_ver->tcon_ver);
+}
+
+static void spdc_set_update_concurrency(mxc_spdc_t *fb_data)
+{
+ u32 concur_mode;
+
+ concur_mode = fb_data->fresh_param.concur & 0xFF;
+ concur_mode |= (SPDC_LUT_MODE_OFFSET << 8);
+
+ __raw_writel(concur_mode, fb_data->hwp + SPDC_LUT_PARA_UPDATE);
+}
+
+static bool is_preprocess_list_full(mxc_spdc_t *fb_data)
+{
+ /* Check to see if preprocess are full in this list */
+ if (fb_data->upd_preprocess_num >= SPDC_MAX_NUM_PREPROCESS)
+ return true;
+ else
+ return false;
+}
+
+static void spdc_submit_update(mxc_spdc_t *fb_data)
+{
+ fb_data->updates_active = true;
+
+ spdc_set_nextbuf_addr(fb_data);
+ spdc_set_update_coord(fb_data);
+ spdc_set_update_dimensions(fb_data);
+ spdc_set_update_temper(fb_data);
+ spdc_trigger_update(fb_data);
+}
+
+static int spdc_init_sequence(mxc_spdc_t *fb_data)
+{
+ struct fb_var_screeninfo *screeninfo = &fb_data->spdc_fb_var;
+ struct spdc_buffer_addr *buf_addr = &fb_data->fresh_param.buf_addr;
+ struct imx_spdc_panel_init_set *init_set =
+ fb_data->pdata->spdc_mode->init_set;
+ u32 xres, yres;
+ int ret = -EFAULT;
+
+ /*init spdc power*/
+ spdc_powerup(fb_data);
+
+ /* enable all interrupt */
+ spdc_intr_stat_clear(fb_data, SPDC_INT_STA_CLR);
+ spdc_intr_enable(fb_data, SPDC_IRQ_ALL_MASK);
+ /* set ACC concurrency update mode */
+ if (fb_data->fresh_param.concur)
+ spdc_set_update_concurrency(fb_data);
+
+ /* program SPDC register and trigger to process buffer*/
+ buf_addr->next_buf_phys_addr = fb_data->phy_next_buf;
+ buf_addr->cur_buf_phys_addr = fb_data->phy_current_buf;
+ buf_addr->pre_buf_phys_addr = fb_data->phy_pre_buf;
+ buf_addr->frm_cnt_buf_phys_addr = fb_data->phy_cnt_buf;
+ buf_addr->lut_buf_phys_addr = fb_data->phy_lut_buf;
+
+ /* Use unrotated (native) width/height */
+ if ((screeninfo->rotate == FB_ROTATE_CW) ||
+ (screeninfo->rotate == FB_ROTATE_CCW)) {
+ xres = screeninfo->yres;
+ yres = screeninfo->xres;
+ } else {
+ xres = screeninfo->xres;
+ yres = screeninfo->yres;
+ }
+ fb_data->fresh_param.update_region.left = 0;
+ fb_data->fresh_param.update_region.top = 0;
+ fb_data->fresh_param.update_region.width = xres;
+ fb_data->fresh_param.update_region.height = yres;
+
+ /* set panel temperature as environment temperature */
+ fb_data->fresh_param.temper = SPDC_DEFAULT_TEMP;
+ /* set waveform mode */
+ fb_data->fresh_param.wave_mode = SPDC_WAV_MODE_DEFAULT;
+
+ spdc_set_update_coord(fb_data);
+ spdc_set_update_dimensions(fb_data);
+
+ spdc_set_update_temper(fb_data);
+
+ spdc_set_nextbuf_addr(fb_data);
+ spdc_set_curbuf_addr(fb_data);
+ spdc_set_prebuf_addr(fb_data);
+ spdc_set_cntbuf_addr(fb_data);
+
+ /* load waveform*/
+ spdc_set_lutbuf_addr(fb_data);
+ ret = wait_for_completion_timeout(&fb_data->lut_down,
+ msecs_to_jiffies(4000));
+ if (!ret) {
+ dev_err(fb_data->dev,
+ "Timed out for lut!\n");
+ return ret;
+ }
+
+ /* init SPDC setting, the setting get from platform data */
+ fb_data->panel_set.yoe_pol = init_set->yoe_pol;
+ fb_data->panel_set.dual_gate = init_set->dual_gate;
+ fb_data->panel_set.ud = init_set->ud;
+ fb_data->panel_set.rl = init_set->rl;
+ fb_data->panel_set.data_filter_n = init_set->data_filter_n;
+ fb_data->panel_set.rgbw_mode_enable = init_set->rgbw_mode_enable;
+ fb_data->panel_set.hburst_len_en = init_set->hburst_len_en;
+ ret = update_panel_init_set(fb_data);
+
+ return ret;
+}
+
+static u32 mxc_spdc_partial_refresh_low(mxc_spdc_t *fb_data, void *buffer)
+{
+ u8 *fresh_addr;
+ void *pattern = buffer;
+ struct partial_refresh_param *fresh_param = &fb_data->fresh_param;
+ u32 fresh_size;
+ int ret = 0;
+
+ fb_data->updates_active = true;
+
+ fresh_addr = (u8 *)(fb_data->virt_start) +
+ (fresh_param->update_region.top * fresh_param->stride) +
+ ((fresh_param->update_region.left * fb_data->default_bpp) >> 3);
+ fresh_size = (u32)((fresh_param->update_region.width *
+ fresh_param->update_region.height * fb_data->default_bpp) >> 3);
+
+ if (buffer != NULL) {
+ while (fresh_size > 0) {
+ memcpy((void *)fresh_addr, pattern,
+ fresh_param->update_region.width);
+ fresh_size -= fresh_param->update_region.width;
+ fresh_addr += fresh_param->update_region.top *
+ fresh_param->stride;
+ pattern += fresh_param->update_region.width;
+ }
+ }
+
+ /* program SPDC register and trigger to process buffer*/
+ fb_data->fresh_param.buf_addr.next_buf_phys_addr =
+ fb_data->info.fix.smem_start + fb_data->fb_offset;
+ fb_data->fresh_param.wave_mode = fb_data->wv_modes.mode_init;
+ spdc_submit_update(fb_data);
+
+ ret = wait_for_completion_timeout(&fb_data->update_finish,
+ msecs_to_jiffies(3000));
+ if (!ret) {
+ dev_err(fb_data->dev,
+ "display update timeout!\n");
+ return -ETIMEDOUT;
+ }
+
+ return ret;
+}
+
+static u32 spdc_fb_dev_init(mxc_spdc_t *fb_data)
+{
+ fb_data->auto_mode = AUTO_UPDATE_MODE_REGION_MODE;
+ fb_data->fresh_param.wave_mode = SPDC_WAV_MODE_0;
+ fb_data->operation_mode = SPDC_NO_OPERATION;
+ fb_data->is_deep_fresh = false;
+
+ /* Init the concurrency update */
+ fb_data->fresh_param.concur = 0;
+ fb_data->upd_preprocess_num = 0;
+ fb_data->submit_upd_sta = 0;
+
+ fb_data->fresh_param.temper = SPDC_DEFAULT_TEMP;
+
+ return 0;
+}
+
+/**
+ * mxc_spdc_device_is_busy - check spdc device busy status.
+ * Returns 0 if spdc device is idle.
+ */
+static int mxc_spdc_device_is_busy(mxc_spdc_t *fb_data)
+{
+ u32 status;
+ u32 orig_jiffies = jiffies;
+
+ while (1) {
+ status = __raw_readl(fb_data->hwp + SPDC_STATUS);
+ if ((status & SPDC_PANEL_STAUTS_BUSY) &&
+ ((status & 0xF0) == SPDC_TCON_STATUS_IDLE))
+ break;
+
+ if (signal_pending(current)) {
+ dev_dbg(fb_data->dev, "SPDC Interrupted\n");
+ return -EINTR;
+ }
+
+ if (time_after(jiffies, orig_jiffies +
+ msecs_to_jiffies(3000))) {
+ dev_dbg(fb_data->dev, "SPDC is busy\n");
+ return -ETIMEDOUT;
+ }
+
+ schedule();
+ }
+
+ return 0;
+}
+
+static bool is_free_list_full(mxc_spdc_t *fb_data)
+{
+ int count = 0;
+ struct update_data_list *plist;
+
+ /* Count buffers in free buffer list */
+ list_for_each_entry(plist, &fb_data->upd_buf_free_list, list)
+ count++;
+
+ /* Check to see if all buffers are in this list */
+ if (count == fb_data->max_num_updates)
+ return true;
+ else
+ return false;
+}
+
+static void spdc_draw_mode0(mxc_spdc_t *fb_data)
+{
+ struct mxcfb_update_data update;
+ struct mxcfb_update_marker_data upd_marker_data;
+ struct fb_var_screeninfo *screeninfo = &fb_data->spdc_fb_var;
+ u32 xres, yres;
+ int ret;
+
+ fb_data->fresh_param.buf_addr.next_buf_phys_addr =
+ fb_data->phys_start;
+
+ fb_data->hw_ready = true;
+ fb_data->hw_initializing = false;
+
+ /* Use unrotated (native) width/height */
+ if ((screeninfo->rotate == FB_ROTATE_CW) ||
+ (screeninfo->rotate == FB_ROTATE_CCW)) {
+ xres = screeninfo->yres;
+ yres = screeninfo->xres;
+ } else {
+ xres = screeninfo->xres;
+ yres = screeninfo->yres;
+ }
+
+ update.update_region.left = 0;
+ update.update_region.width = xres;
+ update.update_region.top = 0;
+ update.update_region.height = yres;
+ update.update_mode = UPDATE_MODE_FULL;
+ update.waveform_mode = fb_data->wv_modes.mode_init;
+ update.update_marker = INIT_UPDATE_MARKER;
+ update.temp = SPDC_DEFAULT_TEMP;
+ update.flags = 0;
+
+ upd_marker_data.update_marker = update.update_marker;
+
+ mxc_spdc_fb_send_update(&update, &fb_data->info);
+
+ /* Block on initial update */
+ ret = mxc_spdc_fb_wait_update_complete(&upd_marker_data,
+ &fb_data->info);
+ if (ret < 0)
+ dev_err(fb_data->dev,
+ "Wait for update complete failed, Err:%d", ret);
+}
+
+static void
+spdc_fb_fw_handler(const struct firmware *fw, void *context)
+{
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)context;
+ struct clk *spdc_parent;
+ unsigned long rounded_parent_rate, spdc_pix_rate,
+ rounded_pix_clk, target_pix_clk;
+ u8 *wv_file;
+ int ret;
+
+ if (fw == NULL) {
+ /* If default FW file load failed, we give up */
+ if (fb_data->fw_default_load)
+ return;
+
+ /* Try to load default waveform */
+ fb_data->fw_default_load = true;
+
+ ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ fb_data->fw_str, fb_data->dev, GFP_KERNEL,
+ fb_data, spdc_fb_fw_handler);
+ if (ret) {
+ dev_err(fb_data->dev,
+ "Failed to load waveform image with err %d\n", ret);
+ return;
+ }
+ }
+
+ wv_file = (u8 *)fw->data;
+ memcpy(fb_data->virt_lut_buf, wv_file, fw->size);
+
+ check_waveform((u32 *)wv_file, (u32 *)fb_data->virt_lut_buf,
+ fw->size / 4);
+ release_firmware(fw);
+
+ /* Enable clocks to access SPDC regs */
+ clk_enable(fb_data->spdc_clk_axi);
+
+ target_pix_clk = fb_data->cur_mode->vmode->pixclock;
+ /* Enable pix clk for SPDC */
+ clk_enable(fb_data->spdc_clk_pix);
+ rounded_pix_clk = clk_round_rate(fb_data->spdc_clk_pix, target_pix_clk);
+
+ if (((rounded_pix_clk >= target_pix_clk + target_pix_clk/100) ||
+ (rounded_pix_clk <= target_pix_clk - target_pix_clk/100))) {
+ /* Can't get close enough without changing parent clk */
+ spdc_parent = clk_get_parent(fb_data->spdc_clk_pix);
+ rounded_parent_rate =
+ clk_round_rate(spdc_parent, target_pix_clk);
+
+ spdc_pix_rate = target_pix_clk;
+ while (spdc_pix_rate < rounded_parent_rate)
+ spdc_pix_rate *= 2;
+ clk_set_rate(spdc_parent, spdc_pix_rate);
+
+ rounded_pix_clk =
+ clk_round_rate(fb_data->spdc_clk_pix, target_pix_clk);
+ if (((rounded_pix_clk >= target_pix_clk + target_pix_clk/100) ||
+ (rounded_pix_clk <= target_pix_clk - target_pix_clk/100)))
+ /* Still can't get a good clock, provide warning */
+ dev_err(fb_data->dev,
+ "Unable to get an accurate SPDC pix clk"
+ "desired = %lu, actual = %lu\n", target_pix_clk,
+ rounded_pix_clk);
+ }
+
+ clk_set_rate(fb_data->spdc_clk_pix, rounded_pix_clk);
+
+ if (!spdc_init_sequence(fb_data))
+ return;
+
+ /* display log on picture */
+ spdc_draw_mode0(fb_data);
+}
+
+
+static int spdc_fb_init_hw(struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ int ret;
+
+ fb_data->fw_default_load = false;
+ /*
+ * Create fw search string based on ID string in selected videomode.
+ * Format is "imx/spdc_[wave_timing].fw: spdc_pvi.fw, spdc_auo.fw"
+ */
+ if (fb_data->cur_mode) {
+ memset(fb_data->fw_str, 0, sizeof(fb_data->fw_str));
+ strcat(fb_data->fw_str, "imx/spdc_");
+ strcat(fb_data->fw_str, fb_data->cur_mode->wave_timing);
+ strcat(fb_data->fw_str, ".fw");
+ } else
+ strcat(fb_data->fw_str, "imx/spdc_pvi.fw");
+
+ ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ fb_data->fw_str, fb_data->dev, GFP_KERNEL,
+ fb_data, spdc_fb_fw_handler);
+ if (ret) {
+ dev_err(fb_data->dev,
+ "Failed to load waveform image with err %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int mxc_spdc_partial_refresh(mxc_spdc_t *fb_data, void *buffer)
+{
+ int ret = 0;
+ struct partial_refresh_param *fresh_param = &fb_data->fresh_param;
+
+ if (!fb_data->panel_set.power_ready)
+ spdc_powerup(fb_data);
+
+ if (!fresh_param->blocking) {
+ if (mxc_spdc_device_is_busy(fb_data)) {
+ dev_err(fb_data->dev, "spdc busy!\n");
+ return (u32) -1;
+ }
+ } else {
+ while (mxc_spdc_device_is_busy(fb_data)) {
+ dev_err(fb_data->dev, "Waiting for spdc idle..\n");
+ msleep(500);
+ }
+ }
+
+ ret = mxc_spdc_partial_refresh_low(fb_data, buffer);
+
+ return ret;
+}
+
+static int mxc_operaton_update(mxc_spdc_t *fb_data)
+{
+ int ret = 0;
+ struct partial_refresh_param *fresh_param = &fb_data->fresh_param;
+ u32 operation_mode = fb_data->operation_mode;
+
+ if (!fb_data->panel_set.power_ready)
+ spdc_powerup(fb_data);
+
+ if (operation_mode != SPDC_SW_TCON_RESET) {
+ if (!fresh_param->blocking) {
+ if (mxc_spdc_device_is_busy(fb_data)) {
+ dev_err(fb_data->dev, "spdc busy\n");
+ return (u32) -1;
+ }
+ } else {
+ while (mxc_spdc_device_is_busy(fb_data)) {
+ dev_err(fb_data->dev, "Waiting spdc idle...\n");
+ msleep(500);
+ }
+ }
+ } else
+ operation_mode = SPDC_SW_TCON_RESET_SET;
+
+ /* don't add to queue list */
+ mutex_lock(&fb_data->queue_mutex);
+ __raw_writel(operation_mode, fb_data->hwp + SPDC_OPERATE);
+ mutex_unlock(&fb_data->queue_mutex);
+
+ if (operation_mode == SPDC_SW_TCON_RESET_SET) {
+ dev_dbg(fb_data->dev, "reinit hw\n");
+ mdelay(500);
+
+ fb_data->hw_ready = false;
+ fb_data->operation_mode = SPDC_NO_OPERATION;
+ ret = spdc_fb_init_hw(&fb_data->info);
+ if (ret && !fb_data->hw_ready)
+ dev_err(fb_data->dev, "Failed to init HW!\n");
+ }
+
+ return ret;
+}
+
+static int mxc_spdc_refresh_display(mxc_spdc_t *fb_data)
+{
+ struct partial_refresh_param *fresh_param = &fb_data->fresh_param;
+ u32 operation_mode = fb_data->operation_mode;
+ int ret = 0;
+
+ fresh_param->update_region.left = 0;
+ fresh_param->update_region.top = 0;
+ fresh_param->update_region.width = fb_data->spdc_fb_var.xres;
+ fresh_param->update_region.height = fb_data->spdc_fb_var.yres;
+ fresh_param->stride = (fb_data->spdc_fb_var.xres *
+ fb_data->spdc_fb_var.bits_per_pixel) >> 3;
+
+ if (operation_mode && operation_mode < SPDC_FULL_REFRESH)
+ ret = mxc_operaton_update(fb_data);
+ else
+ ret = mxc_spdc_partial_refresh(fb_data, NULL);
+
+ return ret;
+}
+
+static void mxc_spdc_find_match_mode(mxc_spdc_t *fb_data)
+{
+ struct imx_spdc_fb_mode *spdc_mode =
+ &fb_data->pdata->spdc_mode[0];
+ const struct mxc_spdc_resolution_map_para *spdc_res_map;
+ u32 i = 0;
+ u32 j = 0;
+ u32 default_mode = 0xFF;
+
+ if (fb_data->panel_set.rgbw_mode_enable)
+ spdc_res_map = &spdc_rgbw_res_map[0];
+ else
+ spdc_res_map = &spdc_gray_res_map[0];
+
+ while (spdc_mode != NULL) {
+ while (spdc_res_map[j].resolution != 0xFFFF) {
+ if (spdc_mode->vmode->xres == spdc_res_map[j].res_x
+ && spdc_mode->vmode->yres == spdc_res_map[j].res_y) {
+ fb_data->panel_set.resolution =
+ spdc_res_map[j].resolution;
+ default_mode = i;
+ break;
+ }
+ j++;
+ }
+
+ if (default_mode != 0xFF)
+ break;
+ j = 0;
+ i++;
+ spdc_mode = &fb_data->pdata->spdc_mode[i];
+ }
+
+ fb_data->cur_mode = spdc_mode;
+}
+
+static int mxc_spdc_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ unsigned long page, pos;
+
+ if (offset + size > info->fix.smem_len)
+ return -EINVAL;
+
+ pos = (unsigned long)info->fix.smem_start + offset;
+
+ /* make buffers bufferable */
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+ vma->vm_flags |= VM_RESERVED | VM_IO;
+
+ while (size > 0) {
+ page = pos;
+ if (io_remap_pfn_range(vma, start, page >> PAGE_SHIFT,
+ PAGE_SIZE, vma->vm_page_prot))
+ return -EAGAIN;
+
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+
+ return 0;
+}
+
+static inline u_int _chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+ chan &= 0xffff;
+ chan >>= 16 - bf->length;
+ return chan << bf->offset;
+}
+
+static int mxc_spdc_fb_setcolreg(u_int regno, u_int red, u_int green,
+ u_int blue, u_int transp, struct fb_info *info)
+{
+ if (regno >= 256) /* no. of hw registers */
+ return 1;
+
+ /* grayscale works only partially under directcolor */
+ if (info->var.grayscale) {
+ /* grayscale = 0.30*R + 0.59*G + 0.11*B */
+ red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
+ }
+
+#define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16)
+ switch (info->fix.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ case FB_VISUAL_PSEUDOCOLOR:
+ red = CNVT_TOHW(red, info->var.red.length);
+ green = CNVT_TOHW(green, info->var.green.length);
+ blue = CNVT_TOHW(blue, info->var.blue.length);
+ transp = CNVT_TOHW(transp, info->var.transp.length);
+ break;
+ case FB_VISUAL_DIRECTCOLOR:
+ red = CNVT_TOHW(red, 8); /* expect 8 bit DAC */
+ green = CNVT_TOHW(green, 8);
+ blue = CNVT_TOHW(blue, 8);
+ /* hey, there is bug in transp handling... */
+ transp = CNVT_TOHW(transp, 8);
+ break;
+ }
+#undef CNVT_TOHW
+ /* Truecolor has hardware independent palette */
+ if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
+ if (regno >= 16)
+ return 1;
+
+ ((u32 *) (info->pseudo_palette))[regno] =
+ (red << info->var.red.offset) |
+ (green << info->var.green.offset) |
+ (blue << info->var.blue.offset) |
+ (transp << info->var.transp.offset);
+ }
+
+ return 0;
+}
+
+void mxc_spdc_fb_flush_updates(mxc_spdc_t *fb_data)
+{
+ int ret;
+
+ /* Grab queue lock to prevent any new updates from being submitted */
+ mutex_lock(&fb_data->queue_mutex);
+
+ /*
+ * 3 places to check for updates that are active or pending:
+ * 1) Updates in the pending list
+ * 2) Update buffers in use (e.g., PxP processing)
+ * 3) Active updates to panel - We can key off of SPDC
+ * power state to know if we have active updates.
+ */
+ if (!list_empty(&fb_data->upd_pending_list) ||
+ !is_free_list_full(fb_data) ||
+ (fb_data->updates_active == true)) {
+ /* Initialize event signalling updates are done */
+ init_completion(&fb_data->updates_done);
+ fb_data->waiting_for_idle = true;
+
+ mutex_unlock(&fb_data->queue_mutex);
+ /* Wait for any currently active updates to complete */
+ ret = wait_for_completion_timeout(&fb_data->updates_done,
+ msecs_to_jiffies(8000));
+ if (!ret)
+ dev_err(fb_data->dev,
+ "Flush updates timeout! ret = 0x%x\n", ret);
+
+ mutex_lock(&fb_data->queue_mutex);
+ fb_data->waiting_for_idle = false;
+ }
+
+ mutex_unlock(&fb_data->queue_mutex);
+}
+
+static int mxc_spdc_fb_setcmap(struct fb_cmap *cmap, struct fb_info *info)
+{
+ int count, index, r;
+ u16 *red, *green, *blue, *transp;
+ u16 trans = 0xffff;
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ int i;
+
+ dev_dbg(fb_data->dev, "setcmap\n");
+
+ if (info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) {
+ /* Only support an 8-bit, 256 entry lookup */
+ if (cmap->len != 256)
+ return 1;
+
+ mxc_spdc_fb_flush_updates(fb_data);
+
+ mutex_lock(&fb_data->pxp_mutex);
+ /*
+ * Store colormap in pxp_conf structure for later transmit
+ * to PxP during update process to convert gray pixels.
+ *
+ * Since red=blue=green for pseudocolor visuals, we can
+ * just use red values.
+ */
+ for (i = 0; i < 256; i++)
+ fb_data->pxp_conf.proc_data.lut_map[i] =
+ cmap->red[i] & 0xFF;
+
+ fb_data->pxp_conf.proc_data.lut_map_updated = true;
+
+ mutex_unlock(&fb_data->pxp_mutex);
+ } else {
+ red = cmap->red;
+ green = cmap->green;
+ blue = cmap->blue;
+ transp = cmap->transp;
+ index = cmap->start;
+
+ for (count = 0; count < cmap->len; count++) {
+ if (transp)
+ trans = *transp++;
+ r = mxc_spdc_fb_setcolreg(index++, *red++,
+ *green++, *blue++, trans, info);
+ if (r != 0)
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+static void adjust_coordinates(u32 xres, u32 yres, u32 rotation,
+ struct mxcfb_rect *update_region, struct mxcfb_rect *adj_update_region)
+{
+ u32 temp;
+
+ /* If adj_update_region == NULL, pass result back in update_region */
+ /* If adj_update_region == valid, use it to pass back result */
+ if (adj_update_region)
+ switch (rotation) {
+ case FB_ROTATE_UR:
+ adj_update_region->top = update_region->top;
+ adj_update_region->left = update_region->left;
+ adj_update_region->width = update_region->width;
+ adj_update_region->height = update_region->height;
+ break;
+ case FB_ROTATE_CW:
+ adj_update_region->top = update_region->left;
+ adj_update_region->left = yres -
+ (update_region->top + update_region->height);
+ adj_update_region->width = update_region->height;
+ adj_update_region->height = update_region->width;
+ break;
+ case FB_ROTATE_UD:
+ adj_update_region->width = update_region->width;
+ adj_update_region->height = update_region->height;
+ adj_update_region->top = yres -
+ (update_region->top + update_region->height);
+ adj_update_region->left = xres -
+ (update_region->left + update_region->width);
+ break;
+ case FB_ROTATE_CCW:
+ adj_update_region->left = update_region->top;
+ adj_update_region->top = xres -
+ (update_region->left + update_region->width);
+ adj_update_region->width = update_region->height;
+ adj_update_region->height = update_region->width;
+ break;
+ }
+ else
+ switch (rotation) {
+ case FB_ROTATE_UR:
+ /* No adjustment needed */
+ break;
+ case FB_ROTATE_CW:
+ temp = update_region->top;
+ update_region->top = update_region->left;
+ update_region->left = yres -
+ (temp + update_region->height);
+ temp = update_region->width;
+ update_region->width = update_region->height;
+ update_region->height = temp;
+ break;
+ case FB_ROTATE_UD:
+ update_region->top = yres -
+ (update_region->top + update_region->height);
+ update_region->left = xres -
+ (update_region->left + update_region->width);
+ break;
+ case FB_ROTATE_CCW:
+ temp = update_region->left;
+ update_region->left = update_region->top;
+ update_region->top = xres -
+ (temp + update_region->width);
+ temp = update_region->width;
+ update_region->width = update_region->height;
+ update_region->height = temp;
+ break;
+ }
+}
+
+static int mxc_spdc_fb_set_fix(struct fb_info *info)
+{
+ struct fb_fix_screeninfo *fix = &info->fix;
+ struct fb_var_screeninfo *var = &info->var;
+
+ fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
+
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->accel = FB_ACCEL_NONE;
+ if (var->grayscale)
+ fix->visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
+ else
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ fix->xpanstep = 1;
+ fix->ypanstep = 1;
+
+ return 0;
+}
+
+static int mxc_spdc_fb_set_par(struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ struct fb_var_screeninfo *screeninfo = &fb_data->info.var;
+ struct imx_spdc_fb_mode *spdc_modes = fb_data->pdata->spdc_mode;
+ struct pxp_config_data *pxp_conf = &fb_data->pxp_conf;
+ struct pxp_proc_data *proc_data = &pxp_conf->proc_data;
+ int i, ret;
+ __u32 xoffset_old, yoffset_old;
+
+ /*
+ * Can't change the FB parameters until current updates have completed.
+ * This function returns when all active updates are done.
+ */
+ mxc_spdc_fb_flush_updates(fb_data);
+
+ mutex_lock(&fb_data->queue_mutex);
+ /*
+ * Set all screeninfo except for xoffset/yoffset
+ * Subsequent call to pan_display will handle those.
+ */
+ xoffset_old = fb_data->spdc_fb_var.xoffset;
+ yoffset_old = fb_data->spdc_fb_var.yoffset;
+ fb_data->spdc_fb_var = *screeninfo;
+ fb_data->spdc_fb_var.xoffset = xoffset_old;
+ fb_data->spdc_fb_var.yoffset = yoffset_old;
+ mutex_unlock(&fb_data->queue_mutex);
+
+ mutex_lock(&fb_data->pxp_mutex);
+
+ /*
+ * Update PxP config data (used to process FB regions for updates)
+ * based on FB info and processing tasks required
+ */
+ /* Initialize non-channel-specific PxP parameters */
+ proc_data->drect.left = proc_data->srect.left = 0;
+ proc_data->drect.top = proc_data->srect.top = 0;
+ proc_data->drect.width = proc_data->srect.width = screeninfo->xres;
+ proc_data->drect.height = proc_data->srect.height = screeninfo->yres;
+ proc_data->scaling = 0;
+ proc_data->hflip = 0;
+ proc_data->vflip = 0;
+ proc_data->rotate = screeninfo->rotate;
+ proc_data->bgcolor = 0;
+ proc_data->overlay_state = 0;
+ proc_data->lut_transform = PXP_LUT_NONE;
+
+ /*
+ * configure S0 channel parameters
+ * Parameters should match FB format/width/height
+ */
+ if (screeninfo->grayscale)
+ pxp_conf->s0_param.pixel_fmt = PXP_PIX_FMT_GY04;
+ else {
+ switch (screeninfo->bits_per_pixel) {
+ case 16:
+ pxp_conf->s0_param.pixel_fmt = PXP_PIX_FMT_RGB565;
+ break;
+ case 24:
+ pxp_conf->s0_param.pixel_fmt = PXP_PIX_FMT_RGB24;
+ break;
+ case 32:
+ pxp_conf->s0_param.pixel_fmt = PXP_PIX_FMT_RGB32;
+ break;
+ default:
+ pxp_conf->s0_param.pixel_fmt = PXP_PIX_FMT_RGB565;
+ break;
+ }
+ }
+ pxp_conf->s0_param.width = screeninfo->xres_virtual;
+ pxp_conf->s0_param.height = screeninfo->yres;
+ pxp_conf->s0_param.color_key = -1;
+ pxp_conf->s0_param.color_key_enable = false;
+
+ /*
+ * Initialize Output channel parameters
+ * Output is Y-only greyscale
+ * Output width/height will vary based on update region size
+ */
+ pxp_conf->out_param.width = screeninfo->xres;
+ pxp_conf->out_param.height = screeninfo->yres;
+ pxp_conf->out_param.pixel_fmt = PXP_PIX_FMT_GY04;
+
+ mutex_unlock(&fb_data->pxp_mutex);
+
+ /* active new config, If HW not yet initialized,
+ * check to see if we are being sent
+ * an initialization request.
+ */
+ if (!fb_data->hw_ready) {
+ struct fb_videomode mode;
+ bool found_match = false;
+ u32 xres_temp;
+
+ fb_var_to_videomode(&mode, screeninfo);
+
+ /* When comparing requested fb mode,
+ * we need to use unrotated dimensions
+ */
+ if ((screeninfo->rotate == FB_ROTATE_CW) ||
+ (screeninfo->rotate == FB_ROTATE_CCW)) {
+ xres_temp = mode.xres;
+ mode.xres = mode.yres;
+ mode.yres = xres_temp;
+ }
+
+ /* Match videomode against spdc modes */
+ for (i = 0; i < fb_data->pdata->num_modes; i++) {
+ if (!fb_mode_is_equal(spdc_modes[i].vmode, &mode))
+ continue;
+ fb_data->cur_mode = &spdc_modes[i];
+ found_match = true;
+ break;
+ }
+
+ if (!found_match) {
+ dev_err(fb_data->dev,
+ "Failed to match requested video mode\n");
+ return EINVAL;
+ }
+
+ /* Initialize SPDC settings and init panel */
+ ret =
+ spdc_fb_init_hw((struct fb_info *)fb_data);
+ if (ret) {
+ dev_err(fb_data->dev,
+ "Failed to load panel waveform data\n");
+ return ret;
+ }
+ }
+
+ mxc_spdc_fb_set_fix(info);
+
+ return 0;
+}
+
+static int
+mxc_spdc_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+
+ if (!var->xres)
+ var->xres = 1;
+ if (!var->yres)
+ var->yres = 1;
+
+ if (var->xres_virtual < var->xoffset + var->xres)
+ var->xres_virtual = var->xoffset + var->xres;
+ if (var->yres_virtual < var->yoffset + var->yres)
+ var->yres_virtual = var->yoffset + var->yres;
+
+ if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&
+ (var->bits_per_pixel != 16) && (var->bits_per_pixel != 8) &&
+ (var->bits_per_pixel != 4))
+ var->bits_per_pixel = SPDC_DEFAULT_BPP;
+
+ switch (var->bits_per_pixel) {
+ case 4:
+ var->red.offset = 0;
+ var->red.length = var->bits_per_pixel;
+ var->green = var->red;
+ var->blue = var->red;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ case 8:
+ if (var->grayscale != 0) {
+ var->red.length = 8;
+ var->red.offset = 0;
+ var->red.msb_right = 0;
+
+ var->green = var->red;
+ var->blue = var->red;
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ } else {
+ var->red.length = 3;
+ var->red.offset = 5;
+ var->red.msb_right = 0;
+
+ var->green.length = 3;
+ var->green.offset = 2;
+ var->green.msb_right = 0;
+
+ var->blue.length = 2;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ }
+ break;
+ case 16:
+ var->red.length = 5;
+ var->red.offset = 11;
+ var->red.msb_right = 0;
+
+ var->green.length = 6;
+ var->green.offset = 5;
+ var->green.msb_right = 0;
+
+ var->blue.length = 5;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 24:
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 32:
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 8;
+ var->transp.offset = 24;
+ var->transp.msb_right = 0;
+ break;
+ }
+
+ switch (var->rotate) {
+ case FB_ROTATE_UR:
+ case FB_ROTATE_UD:
+ var->xres = fb_data->native_width;
+ var->yres = fb_data->native_height;
+ break;
+ case FB_ROTATE_CW:
+ case FB_ROTATE_CCW:
+ var->xres = fb_data->native_height;
+ var->yres = fb_data->native_width;
+ break;
+ default:
+ /* Invalid rotation value */
+ var->rotate = 0;
+ dev_dbg(fb_data->dev, "Invalid rotation request\n");
+ return -EINVAL;
+ }
+
+ var->xres_virtual = ALIGN(var->xres, 32);
+ var->yres_virtual = ALIGN(var->yres, 128) * fb_data->num_screens;
+
+ var->height = -1;
+ var->width = -1;
+
+ return 0;
+}
+
+void mxc_spdc_fb_set_waveform_modes(struct mxcfb_waveform_modes *modes,
+ struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = info ?
+ (mxc_spdc_t *)info:g_fb_data;
+
+ mutex_lock(&fb_data->queue_mutex);
+
+ memcpy(&fb_data->wv_modes, modes, sizeof(struct mxcfb_waveform_modes));
+
+ mutex_unlock(&fb_data->queue_mutex);
+}
+EXPORT_SYMBOL(mxc_spdc_fb_set_waveform_modes);
+
+/* To stick with non-fractional degrees for the sake
+ * of API consistency with EPDC.
+ */
+int mxc_spdc_fb_set_temperature(int temperature, struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = info ?
+ (mxc_spdc_t *)info:g_fb_data;
+ s8 temper = (s8)(temperature & 0xFF) << 1;
+
+ mutex_lock(&fb_data->queue_mutex);
+
+ if (temper > -110 && temper < 200)
+ __raw_writel(temper, fb_data->hwp + SPDC_TEMP_INFO);
+ else
+ __raw_writel(SPDC_DEFAULT_TEMP, fb_data->hwp + SPDC_TEMP_INFO);
+
+ mutex_unlock(&fb_data->queue_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(mxc_spdc_fb_set_temperature);
+
+
+int mxc_spdc_fb_set_auto_update(u32 auto_mode, struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = info ?
+ (mxc_spdc_t *)info:g_fb_data;
+
+ dev_dbg(fb_data->dev, "Setting auto update mode to %d\n", auto_mode);
+
+ if ((auto_mode == AUTO_UPDATE_MODE_AUTOMATIC_MODE)
+ || (auto_mode == AUTO_UPDATE_MODE_REGION_MODE))
+ fb_data->auto_mode = auto_mode;
+ else {
+ dev_err(fb_data->dev, "Invalid auto update mode parameter.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(mxc_spdc_fb_set_auto_update);
+
+
+int mxc_spdc_fb_set_upd_scheme(u32 upd_scheme, struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = info ?
+ (mxc_spdc_t *)info:g_fb_data;
+
+ dev_dbg(fb_data->dev, "Setting optimization level to %d\n", upd_scheme);
+
+ /*
+ * Can't change the scheme until current updates have completed.
+ * This function returns when all active updates are done.
+ */
+ mxc_spdc_fb_flush_updates(fb_data);
+
+ if ((upd_scheme == UPDATE_SCHEME_SNAPSHOT)
+ || (upd_scheme == UPDATE_SCHEME_QUEUE)
+ || (upd_scheme == UPDATE_SCHEME_QUEUE_AND_MERGE))
+ fb_data->upd_scheme = upd_scheme;
+ else {
+ dev_err(fb_data->dev, "Invalid update scheme specified.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(mxc_spdc_fb_set_upd_scheme);
+
+/* Callback function triggered after PxP receives an EOF interrupt */
+static void pxp_dma_done(void *arg)
+{
+ struct pxp_tx_desc *tx_desc = to_tx_desc(arg);
+ struct dma_chan *chan = tx_desc->txd.chan;
+ struct pxp_channel *pxp_chan = to_pxp_channel(chan);
+ mxc_spdc_t *fb_data = pxp_chan->client;
+
+ /* This call will signal wait_for_completion_timeout()
+ * in send_buffer_to_pxp
+ */
+ complete(&fb_data->pxp_tx_cmpl);
+}
+
+static bool chan_filter(struct dma_chan *chan, void *arg)
+{
+ if (imx_dma_is_pxp(chan))
+ return true;
+ else
+ return false;
+}
+
+/* Function to request PXP DMA channel */
+static int pxp_chan_init(mxc_spdc_t *fb_data)
+{
+ dma_cap_mask_t mask;
+ struct dma_chan *chan;
+
+ /*
+ * Request a free channel
+ */
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ dma_cap_set(DMA_PRIVATE, mask);
+ chan = dma_request_channel(mask, chan_filter, NULL);
+ if (!chan) {
+ dev_err(fb_data->dev, "Unsuccessfully received channel!!!!\n");
+ return -EBUSY;
+ }
+
+ fb_data->pxp_chan = to_pxp_channel(chan);
+ fb_data->pxp_chan->client = fb_data;
+
+ init_completion(&fb_data->pxp_tx_cmpl);
+
+ return 0;
+}
+
+/*
+ * Function to call PxP DMA driver and send our latest FB update region
+ * through the PxP and out to an intermediate buffer.
+ * Note: This is a blocking call, so upon return the PxP tx should be complete.
+ */
+static int pxp_process_update(mxc_spdc_t *fb_data,
+ u32 src_width, u32 src_height,
+ struct mxcfb_rect *update_region)
+{
+ dma_cookie_t cookie;
+ struct scatterlist *sg = fb_data->sg;
+ struct dma_chan *dma_chan;
+ struct pxp_tx_desc *desc;
+ struct dma_async_tx_descriptor *txd;
+ struct pxp_config_data *pxp_conf = &fb_data->pxp_conf;
+ struct pxp_proc_data *proc_data = &fb_data->pxp_conf.proc_data;
+ int i, ret;
+ int length;
+
+ /* First, check to see that we have acquired a PxP Channel object */
+ if (fb_data->pxp_chan == NULL) {
+ /*
+ * PxP Channel has not yet been created and initialized,
+ * so let's go ahead and try
+ */
+ ret = pxp_chan_init(fb_data);
+ if (ret) {
+ /*
+ * PxP channel init failed, and we can't use the
+ * PxP until the PxP DMA driver has loaded, so we abort
+ */
+ dev_err(fb_data->dev, "PxP chan init failed\n");
+ return -ENODEV;
+ }
+ }
+
+ /*
+ * Init completion, so that we
+ * can be properly informed of the completion
+ * of the PxP task when it is done.
+ */
+ init_completion(&fb_data->pxp_tx_cmpl);
+
+ dma_chan = &fb_data->pxp_chan->dma_chan;
+
+ txd = dma_chan->device->device_prep_slave_sg(dma_chan, sg, 2,
+ DMA_TO_DEVICE,
+ DMA_PREP_INTERRUPT);
+ if (!txd) {
+ dev_err(fb_data->info.device,
+ "Error preparing a DMA transaction descriptor.\n");
+ return -EIO;
+ }
+
+ txd->callback_param = txd;
+ txd->callback = pxp_dma_done;
+
+ /*
+ * Configure PxP for processing of new update region
+ * The rest of our config params were set up in
+ * probe() and should not need to be changed.
+ */
+ pxp_conf->s0_param.width = src_width;
+ pxp_conf->s0_param.height = src_height;
+ proc_data->srect.top = update_region->top;
+ proc_data->srect.left = update_region->left;
+ proc_data->srect.width = update_region->width;
+ proc_data->srect.height = update_region->height;
+
+ /*
+ * Because only YUV/YCbCr image can be scaled, configure
+ * drect equivalent to srect, as such do not perform scaling.
+ */
+ proc_data->drect.top = 0;
+ proc_data->drect.left = 0;
+ proc_data->drect.width = proc_data->srect.width;
+ proc_data->drect.height = proc_data->srect.height;
+
+ /* PXP expects rotation in terms of degrees */
+ proc_data->rotate = fb_data->spdc_fb_var.rotate * 90;
+ if (proc_data->rotate > 270)
+ proc_data->rotate = 0;
+
+ pxp_conf->out_param.width = update_region->width;
+ pxp_conf->out_param.height = update_region->height;
+
+ if ((proc_data->rotate == 90) || (proc_data->rotate == 270))
+ pxp_conf->out_param.stride = update_region->height;
+ else
+ pxp_conf->out_param.stride = update_region->width;
+
+ desc = to_tx_desc(txd);
+ length = desc->len;
+ for (i = 0; i < length; i++) {
+ if (i == 0) {/* S0 */
+ memcpy(&desc->proc_data, proc_data,
+ sizeof(struct pxp_proc_data));
+ pxp_conf->s0_param.paddr = sg_dma_address(&sg[0]);
+ memcpy(&desc->layer_param.s0_param, &pxp_conf->s0_param,
+ sizeof(struct pxp_layer_param));
+ } else if (i == 1) {
+ pxp_conf->out_param.paddr = sg_dma_address(&sg[1]);
+ memcpy(&desc->layer_param.out_param,
+ &pxp_conf->out_param,
+ sizeof(struct pxp_layer_param));
+ }
+ /* TODO: OverLay */
+
+ desc = desc->next;
+ }
+
+ /* Submitting our TX starts the PxP processing task */
+ cookie = txd->tx_submit(txd);
+ if (cookie < 0) {
+ dev_err(fb_data->info.device, "Error sending FB through PxP\n");
+ return -EIO;
+ }
+
+ fb_data->txd = txd;
+
+ /* trigger ePxP */
+ dma_async_issue_pending(dma_chan);
+
+ return 0;
+}
+
+static int pxp_complete_update(mxc_spdc_t *fb_data, u32 *hist_stat)
+{
+ int ret;
+ /*
+ * Wait for completion event, which will be set
+ * through our TX callback function.
+ */
+ ret = wait_for_completion_timeout(&fb_data->pxp_tx_cmpl, HZ / 10);
+ if (ret <= 0) {
+ dev_info(fb_data->info.device,
+ "PxP operation failed due to %s\n",
+ ret < 0 ? "user interrupt" : "timeout");
+ dma_release_channel(&fb_data->pxp_chan->dma_chan);
+ fb_data->pxp_chan = NULL;
+ return ret ? : -ETIMEDOUT;
+ }
+
+ if ((fb_data->pxp_conf.proc_data.lut_transform & EPDC_FLAG_USE_CMAP) &&
+ fb_data->pxp_conf.proc_data.lut_map_updated)
+ fb_data->pxp_conf.proc_data.lut_map_updated = false;
+
+ *hist_stat = to_tx_desc(fb_data->txd)->hist_status;
+ dma_release_channel(&fb_data->pxp_chan->dma_chan);
+ fb_data->pxp_chan = NULL;
+
+ dev_dbg(fb_data->dev, "TX completed\n");
+
+ return 0;
+}
+
+static void copy_to_next_buffer(mxc_spdc_t *fb_data,
+ struct update_data_list *upd_data_list)
+{
+ struct mxcfb_update_data *upd_data =
+ &upd_data_list->update_desc->upd_data;
+ unsigned char *temp_buf_ptr = fb_data->virt_addr_copybuf;
+ unsigned char *dst_ptr = upd_data_list->virt_addr;
+ struct mxcfb_rect adj_update_region;
+ int dst_stride, left_offs, line_width;
+ int i;
+
+ switch (fb_data->spdc_fb_var.rotate) {
+ case FB_ROTATE_UR:
+ adj_update_region.top = upd_data->update_region.top;
+ adj_update_region.left = upd_data->update_region.left;
+ adj_update_region.width = upd_data->update_region.width;
+ adj_update_region.height = upd_data->update_region.height;
+ dst_stride = fb_data->spdc_fb_var.xres_virtual / 2;
+ break;
+ case FB_ROTATE_CW:
+ adj_update_region.top = upd_data->update_region.left;
+ adj_update_region.left = fb_data->spdc_fb_var.yres -
+ (upd_data->update_region.top +
+ upd_data->update_region.height);
+ adj_update_region.width = upd_data->update_region.height;
+ adj_update_region.height = upd_data->update_region.width;
+ dst_stride = fb_data->spdc_fb_var.yres / 2;
+ break;
+ case FB_ROTATE_UD:
+ adj_update_region.width = upd_data->update_region.width;
+ adj_update_region.height = upd_data->update_region.height;
+ adj_update_region.top = fb_data->spdc_fb_var.yres -
+ (upd_data->update_region.top + upd_data->update_region.height);
+ adj_update_region.left = fb_data->spdc_fb_var.xres -
+ (upd_data->update_region.left +
+ upd_data->update_region.width);
+ dst_stride = fb_data->spdc_fb_var.xres_virtual / 2;
+ break;
+ case FB_ROTATE_CCW:
+ adj_update_region.left = upd_data->update_region.top;
+ adj_update_region.top = fb_data->spdc_fb_var.xres -
+ (upd_data->update_region.left +
+ upd_data->update_region.width);
+ adj_update_region.width = upd_data->update_region.height;
+ adj_update_region.height = upd_data->update_region.width;
+ dst_stride = fb_data->spdc_fb_var.yres / 2;
+ break;
+ }
+
+ /* pxp output Y4 data.
+ * Copy the raw data to related region in next buffer.
+ */
+ left_offs = adj_update_region.left / 2;
+ line_width = adj_update_region.width / 2;
+
+ dst_ptr += (adj_update_region.top * dst_stride + left_offs);
+ for (i = 0; i < adj_update_region.height; i++) {
+ /* Copy the full line */
+ memcpy(dst_ptr, temp_buf_ptr, line_width);
+
+ dst_ptr += dst_stride;
+ temp_buf_ptr += line_width;
+ }
+}
+
+static int spdc_process_update(struct update_data_list *upd_data_list,
+ mxc_spdc_t *fb_data)
+{
+ /* Region of src buffer for update */
+ struct mxcfb_rect *src_upd_region;
+ struct mxcfb_rect pxp_upd_region;
+ struct update_desc_list *upd_desc_list = upd_data_list->update_desc;
+ u32 src_width, src_height;
+ u32 offset_from_4, bytes_per_pixel;
+ u32 post_rotation_xcoord, post_rotation_ycoord, width_pxp_blocks;
+ u32 pxp_input_offs, pxp_output_offs, pxp_output_shift;
+ bool input_unaligned = false;
+ u32 hist_stat = 0;
+ bool use_temp_buf = false;
+ int ret;
+
+ /*
+ * Are we using FB or an alternate (overlay)
+ * buffer for source of update?
+ */
+ if (upd_desc_list->upd_data.flags & EPDC_FLAG_USE_ALT_BUFFER) {
+ src_width = upd_desc_list->upd_data.alt_buffer_data.width;
+ src_height = upd_desc_list->upd_data.alt_buffer_data.height;
+ src_upd_region =
+ &upd_desc_list->upd_data.alt_buffer_data.alt_update_region;
+ } else {
+ src_width = fb_data->spdc_fb_var.xres_virtual;
+ src_height = fb_data->spdc_fb_var.yres;
+ src_upd_region = &upd_desc_list->upd_data.update_region;
+ }
+
+ if (!(src_upd_region->width == fb_data->spdc_fb_var.xres_virtual &&
+ fb_data->spdc_fb_var.rotate == FB_ROTATE_UR))
+ use_temp_buf = true;
+
+ bytes_per_pixel = fb_data->spdc_fb_var.bits_per_pixel / 8;
+
+ /* Grab pxp_mutex here so that we protect access
+ * to copybuf in addition to the PxP structures */
+ mutex_lock(&fb_data->pxp_mutex);
+
+ offset_from_4 = src_upd_region->left & 0x3;
+ input_unaligned = ((offset_from_4 * bytes_per_pixel % 4) != 0) ?
+ true : false;
+
+ if (input_unaligned) {
+ /* Leave a gap between PxP input addr
+ * and update region pixels
+ */
+ pxp_input_offs =
+ (src_upd_region->top * src_width + src_upd_region->left)
+ * bytes_per_pixel & 0xFFFFFFFC;
+ /* Update region left changes to reflect
+ * relative position to input ptr
+ */
+ pxp_upd_region.left = (offset_from_4 * bytes_per_pixel % 4)
+ / bytes_per_pixel;
+ } else {
+ pxp_input_offs =
+ (src_upd_region->top * src_width + src_upd_region->left)
+ * bytes_per_pixel;
+ pxp_upd_region.left = 0;
+ }
+ pxp_upd_region.top = 0;
+
+ /* Update region dimensions to meet 8x8 pixel requirement */
+ if (fb_data->spdc_fb_var.rotate == 0) {
+ pxp_upd_region.width = ALIGN(src_upd_region->width, 8);
+ pxp_upd_region.height = ALIGN(src_upd_region->height, 8);
+ } else {
+ pxp_upd_region.width =
+ ALIGN(src_upd_region->width + pxp_upd_region.left, 8);
+ pxp_upd_region.height = ALIGN(src_upd_region->height, 8);
+ }
+
+ switch (fb_data->spdc_fb_var.rotate) {
+ case FB_ROTATE_UR:
+ default:
+ post_rotation_xcoord = pxp_upd_region.left;
+ post_rotation_ycoord = pxp_upd_region.top;
+ width_pxp_blocks = pxp_upd_region.width;
+ break;
+ case FB_ROTATE_CW:
+ width_pxp_blocks = pxp_upd_region.height;
+ post_rotation_xcoord = width_pxp_blocks -
+ src_upd_region->height;
+ post_rotation_ycoord = pxp_upd_region.left;
+ break;
+ case FB_ROTATE_UD:
+ width_pxp_blocks = pxp_upd_region.width;
+ post_rotation_xcoord = width_pxp_blocks -
+ src_upd_region->width - pxp_upd_region.left;
+ post_rotation_ycoord = pxp_upd_region.height -
+ src_upd_region->height - pxp_upd_region.top;
+ break;
+ case FB_ROTATE_CCW:
+ width_pxp_blocks = pxp_upd_region.height;
+ post_rotation_xcoord = pxp_upd_region.top;
+ post_rotation_ycoord = pxp_upd_region.width -
+ src_upd_region->width - pxp_upd_region.left;
+ break;
+ }
+
+ /* Update region start coord to force PxP to
+ * process full 8x8 regions
+ */
+ pxp_upd_region.top &= ~0x7;
+ pxp_upd_region.left &= ~0x7;
+
+ pxp_output_shift = ALIGN(post_rotation_xcoord, 8)
+ - post_rotation_xcoord;
+ pxp_output_offs = post_rotation_ycoord * width_pxp_blocks
+ + pxp_output_shift;
+ upd_desc_list->spdc_offs = ALIGN(pxp_output_offs, 8);
+
+ /* Source address either comes from alternate buffer
+ provided in update data, or from the framebuffer. */
+ if (upd_desc_list->upd_data.flags & EPDC_FLAG_USE_ALT_BUFFER)
+ sg_dma_address(&fb_data->sg[0]) =
+ upd_desc_list->upd_data.alt_buffer_data.phys_addr
+ + pxp_input_offs;
+ else {
+ sg_dma_address(&fb_data->sg[0]) =
+ fb_data->info.fix.smem_start + fb_data->fb_offset
+ + pxp_input_offs;
+ sg_set_page(&fb_data->sg[0],
+ virt_to_page(fb_data->info.screen_base),
+ fb_data->info.fix.smem_len,
+ offset_in_page(fb_data->info.screen_base));
+ }
+
+ /* Update sg[1] to point to output of PxP proc task */
+ if (!use_temp_buf) {
+ sg_dma_address(&fb_data->sg[1]) = upd_data_list->phys_addr;
+ sg_set_page(&fb_data->sg[1],
+ virt_to_page(upd_data_list->virt_addr),
+ fb_data->max_pix_size,
+ offset_in_page(upd_data_list->virt_addr));
+ } else {
+ sg_dma_address(&fb_data->sg[1]) = fb_data->phys_addr_copybuf;
+ sg_set_page(&fb_data->sg[1],
+ virt_to_page(fb_data->virt_addr_copybuf),
+ fb_data->max_pix_size,
+ offset_in_page(fb_data->virt_addr_copybuf));
+ }
+
+ /*
+ * Set PxP LUT transform type based on update flags.
+ */
+ fb_data->pxp_conf.proc_data.lut_transform = 0;
+ if (upd_desc_list->upd_data.flags & EPDC_FLAG_ENABLE_INVERSION)
+ fb_data->pxp_conf.proc_data.lut_transform |= PXP_LUT_INVERT;
+ if (upd_desc_list->upd_data.flags & EPDC_FLAG_FORCE_MONOCHROME)
+ fb_data->pxp_conf.proc_data.lut_transform |=
+ PXP_LUT_BLACK_WHITE;
+ if (upd_desc_list->upd_data.flags & EPDC_FLAG_USE_CMAP)
+ fb_data->pxp_conf.proc_data.lut_transform |=
+ PXP_LUT_USE_CMAP;
+
+ /*
+ * Toggle inversion processing if 8-bit
+ * inverted is the current pixel format.
+ */
+ if (fb_data->spdc_fb_var.grayscale == GRAYSCALE_4BIT_INVERTED)
+ fb_data->pxp_conf.proc_data.lut_transform ^= PXP_LUT_INVERT;
+
+ /* This is a blocking call, so upon return PxP tx should be done */
+ ret = pxp_process_update(fb_data, src_width, src_height,
+ &pxp_upd_region);
+ if (ret) {
+ dev_err(fb_data->dev, "Unable to submit PxP update task.\n");
+ mutex_unlock(&fb_data->pxp_mutex);
+ return ret;
+ }
+
+ /* If needed, enable SPDC HW while ePxP is processing */
+ if ((fb_data->power_state == POWER_STATE_OFF)
+ || fb_data->powering_down) {
+ spdc_powerup(fb_data);
+ }
+
+ /* This is a blocking call, so upon return PxP tx should be done */
+ ret = pxp_complete_update(fb_data, &hist_stat);
+ if (ret) {
+ dev_err(fb_data->dev, "Unable to complete PxP update task.\n");
+ mutex_unlock(&fb_data->pxp_mutex);
+ return ret;
+ }
+
+ if (use_temp_buf)
+ copy_to_next_buffer(fb_data, upd_data_list);
+
+ mutex_unlock(&fb_data->pxp_mutex);
+
+ /* Update waveform mode from PxP histogram results */
+ if (upd_desc_list->upd_data.waveform_mode == WAVEFORM_MODE_AUTO) {
+ if (hist_stat & 0x1)
+ upd_desc_list->upd_data.waveform_mode =
+ fb_data->wv_modes.mode_du;
+ else if (hist_stat & 0x2)
+ upd_desc_list->upd_data.waveform_mode =
+ fb_data->wv_modes.mode_gc4;
+ else if (hist_stat & 0x4)
+ upd_desc_list->upd_data.waveform_mode =
+ fb_data->wv_modes.mode_gc8;
+ else if (hist_stat & 0x8)
+ upd_desc_list->upd_data.waveform_mode =
+ fb_data->wv_modes.mode_gc16;
+ else
+ upd_desc_list->upd_data.waveform_mode =
+ fb_data->wv_modes.mode_gc32;
+
+ dev_dbg(fb_data->dev, "hist_stat = 0x%x, new waveform = 0x%x\n",
+ hist_stat, upd_desc_list->upd_data.waveform_mode);
+ }
+
+ return 0;
+}
+
+static bool spdc_submit_concur(mxc_spdc_t *fb_data,
+ struct update_desc_list *update_to_concur)
+{
+ struct mxcfb_update_data *a, *b;
+ struct mxcfb_rect *arect, *brect;
+ struct update_data_list *next_upd;
+ int i = 0;
+
+ a = &update_to_concur->upd_data;
+ arect = &update_to_concur->upd_data.update_region;
+
+ list_for_each_entry(next_upd,
+ &fb_data->upd_buf_preprocess_list, list) {
+ b = &next_upd->update_desc->upd_data;
+ brect = &next_upd->update_desc->upd_data.update_region;
+
+ /* Updates with different waveform
+ * must be executed sequentially.
+ */
+ if (a->waveform_mode != b->waveform_mode)
+ break;
+
+ /*
+ * Concurrency update must has no overlay
+ */
+ if (!(arect->left > (brect->left + brect->width) ||
+ brect->left > (arect->left + arect->width) ||
+ arect->top > (brect->top + brect->height) ||
+ brect->top > (arect->top + arect->height)))
+ break;
+
+ i++;
+ }
+
+ if (i != fb_data->upd_preprocess_num)
+ return false;
+
+ return true;
+}
+
+static int spdc_submit_merge(struct update_desc_list *upd_desc_list,
+ struct update_desc_list *update_to_merge)
+{
+ struct mxcfb_update_data *a, *b;
+ struct mxcfb_rect *arect, *brect;
+ struct mxcfb_rect combine;
+ bool use_flags = false;
+
+ a = &upd_desc_list->upd_data;
+ b = &update_to_merge->upd_data;
+ arect = &upd_desc_list->upd_data.update_region;
+ brect = &update_to_merge->upd_data.update_region;
+
+ /*
+ * Updates with different flags must be executed sequentially.
+ * Halt the merge process to ensure this.
+ */
+ if (a->flags != b->flags) {
+ /*
+ * Special exception: if update regions are identical,
+ * we may be able to merge them.
+ */
+ if ((arect->left != brect->left) ||
+ (arect->top != brect->top) ||
+ (arect->width != brect->width) ||
+ (arect->height != brect->height))
+ return MERGE_BLOCK;
+
+ use_flags = true;
+ }
+
+ if (a->waveform_mode != b->waveform_mode)
+ a->waveform_mode = WAVEFORM_MODE_AUTO;
+
+ if (arect->left > (brect->left + brect->width) ||
+ brect->left > (arect->left + arect->width) ||
+ arect->top > (brect->top + brect->height) ||
+ brect->top > (arect->top + arect->height))
+ return MERGE_FAIL;
+
+ combine.left = arect->left < brect->left ? arect->left : brect->left;
+ combine.top = arect->top < brect->top ? arect->top : brect->top;
+ combine.width = (arect->left + arect->width) >
+ (brect->left + brect->width) ?
+ (arect->left + arect->width - combine.left) :
+ (brect->left + brect->width - combine.left);
+ combine.height = (arect->top + arect->height) >
+ (brect->top + brect->height) ?
+ (arect->top + arect->height - combine.top) :
+ (brect->top + brect->height - combine.top);
+
+ *arect = combine;
+
+ /* Use flags of the later update */
+ if (use_flags)
+ a->flags = b->flags;
+
+ /* Merge markers */
+ list_splice_tail(&update_to_merge->upd_marker_list,
+ &upd_desc_list->upd_marker_list);
+
+ return MERGE_OK;
+
+}
+
+static void spdc_submit_work_func(struct work_struct *work)
+{
+ struct update_desc_list *next_desc, *temp_desc;
+ mxc_spdc_t *fb_data =
+ container_of(work, mxc_spdc_t, spdc_submit_work);
+ struct update_data_list *upd_data_list = NULL;
+ struct mxcfb_rect adj_update_region, *upd_region;
+ struct update_marker_data *current_marker;
+ bool end_merge = false;
+ bool is_transform;
+ u32 update_addr;
+
+ /* Protect access to buffer queues and to update HW */
+ mutex_lock(&fb_data->queue_mutex);
+
+ /* get a buffer from free list */
+ if (list_empty(&fb_data->upd_buf_free_list)) {
+ mutex_unlock(&fb_data->queue_mutex);
+ return;
+ }
+
+ if (fb_data->fresh_param.concur == SPDC_LUT_ACC_MODE) {
+ list_for_each_entry_safe(next_desc, temp_desc,
+ &fb_data->upd_pending_list, list) {
+
+ current_marker =
+ list_entry((&next_desc->upd_marker_list)->next,
+ struct update_marker_data, upd_list);
+
+ if (current_marker->update_marker) {
+ fb_data->submit_upd_sta = 0;
+ break;
+ }
+
+ /* require free buffer list */
+ if (list_empty(&fb_data->upd_buf_free_list)) {
+ dev_dbg(fb_data->dev,
+ "buf free list is empty\n");
+ break;
+ }
+
+ upd_data_list =
+ list_entry(fb_data->upd_buf_free_list.next,
+ struct update_data_list, list);
+ upd_data_list->update_desc = next_desc;
+
+ if (!is_preprocess_list_full(fb_data)) {
+ if (fb_data->cur_update == NULL &&
+ !fb_data->upd_preprocess_num)
+ list_del_init(&next_desc->list);
+ else if (spdc_submit_concur(fb_data,
+ next_desc)) {
+ list_del_init(&next_desc->list);
+ list_add_tail(&upd_data_list->list,
+ &fb_data->upd_buf_preprocess_list);
+ fb_data->upd_preprocess_num++;
+ fb_data->submit_upd_sta =
+ SPDC_CONCUR_UPD;
+ } else
+ break;
+ } else
+ break;
+
+ /* submit to pxp process */
+ list_del_init(&upd_data_list->list);
+ goto pxp_process;
+ }
+
+ upd_data_list = NULL;
+ if (fb_data->submit_upd_sta == SPDC_CONCUR_UPD)
+ fb_data->submit_upd_sta |= SPDC_QUEUE_UPD;
+ else
+ fb_data->submit_upd_sta = SPDC_QUEUE_UPD;
+ }
+
+ list_for_each_entry_safe(next_desc, temp_desc,
+ &fb_data->upd_pending_list, list) {
+
+ if (!upd_data_list) {
+
+ if (list_empty(&fb_data->upd_buf_free_list)) {
+ dev_dbg(fb_data->dev,
+ "buf_free_list is empty\n");
+ break;
+ }
+ upd_data_list =
+ list_entry(fb_data->upd_buf_free_list.next,
+ struct update_data_list, list);
+ list_del_init(&upd_data_list->list);
+ upd_data_list->update_desc = next_desc;
+ list_del_init(&next_desc->list);
+
+ if (fb_data->upd_scheme == UPDATE_SCHEME_QUEUE)
+ break;
+ } else {
+ switch (spdc_submit_merge(upd_data_list->update_desc,
+ next_desc)) {
+ case MERGE_OK:
+ dev_dbg(fb_data->dev,
+ "Update merged [queue]\n");
+ list_del_init(&next_desc->list);
+ kfree(next_desc);
+ break;
+ case MERGE_FAIL:
+ dev_dbg(fb_data->dev,
+ "Update not merged [queue]\n");
+ break;
+ case MERGE_BLOCK:
+ dev_dbg(fb_data->dev,
+ "Merge blocked [collision]\n");
+ end_merge = true;
+ break;
+ }
+
+ if (end_merge)
+ break;
+ }
+ }
+
+ /* Is update list empty? */
+ if (!upd_data_list) {
+ mutex_unlock(&fb_data->queue_mutex);
+ return;
+ }
+
+pxp_process:
+ /*
+ * If no processing required, skip update processing
+ * No processing means:
+ * - FB unrotated
+ * - FB pixel format = 4-bit grayscale
+ * - No look-up transformations (inversion, posterization, etc.)
+ */
+ is_transform = upd_data_list->update_desc->upd_data.flags &
+ (EPDC_FLAG_ENABLE_INVERSION |
+ EPDC_FLAG_FORCE_MONOCHROME | EPDC_FLAG_USE_CMAP) ?
+ true : false;
+ if ((fb_data->spdc_fb_var.rotate == FB_ROTATE_UR) &&
+ (fb_data->spdc_fb_var.grayscale == GRAYSCALE_4BIT) &&
+ !is_transform) {
+
+ /* If needed, enable SPDC HW while ePxP is processing */
+ if ((fb_data->power_state == POWER_STATE_OFF)
+ || fb_data->powering_down)
+ spdc_powerup(fb_data);
+
+ /*
+ * Set update buffer pointer to the start of
+ * the update region in the frame buffer.
+ */
+ upd_region =
+ &upd_data_list->update_desc->upd_data.update_region;
+ update_addr = fb_data->info.fix.smem_start +
+ ((upd_region->top * fb_data->info.var.xres_virtual) +
+ upd_region->left) * fb_data->info.var.bits_per_pixel/8;
+ } else {
+
+ /* Select from PxP output buffers */
+ upd_data_list->phys_addr =
+ fb_data->phys_addr_updbuf[fb_data->upd_buffer_num];
+ upd_data_list->virt_addr =
+ fb_data->virt_addr_updbuf[fb_data->upd_buffer_num];
+ fb_data->upd_buffer_num++;
+ if (fb_data->upd_buffer_num > fb_data->max_num_buffers-1)
+ fb_data->upd_buffer_num = 0;
+ dev_dbg(fb_data->dev,
+ "pxp out addr:0x%x\n", upd_data_list->phys_addr);
+
+ /* Release buffer queues */
+ mutex_unlock(&fb_data->queue_mutex);
+
+ /* Perform PXP processing - SPDC power will also be enabled */
+ if (spdc_process_update(upd_data_list, fb_data)) {
+
+ dev_dbg(fb_data->dev, "PXP processing error.\n");
+ /* Protect access to buffer queues and to update HW */
+ mutex_lock(&fb_data->queue_mutex);
+ list_del_init(&upd_data_list->update_desc->list);
+ kfree(upd_data_list->update_desc);
+ upd_data_list->update_desc = NULL;
+
+ /* Add to free buffer list */
+ list_add_tail(&upd_data_list->list,
+ &fb_data->upd_buf_free_list);
+ /* Release buffer queues */
+ mutex_unlock(&fb_data->queue_mutex);
+ return;
+ }
+
+ /* Protect access to buffer queues and to update HW */
+ mutex_lock(&fb_data->queue_mutex);
+
+ /* output Y4 format */
+ update_addr = upd_data_list->phys_addr +
+ + (upd_data_list->update_desc->spdc_offs / 2);
+ }
+
+ /* Get rotation-adjusted coordinates */
+ adjust_coordinates(fb_data->spdc_fb_var.xres,
+ fb_data->spdc_fb_var.yres, fb_data->spdc_fb_var.rotate,
+ &upd_data_list->update_desc->upd_data.update_region,
+ &adj_update_region);
+
+ /*
+ * Is the working buffer idle?
+ * If the working buffer is busy, we must wait for the resource
+ * to become free.
+ */
+ if (fb_data->cur_update != NULL &&
+ fb_data->submit_upd_sta != SPDC_CONCUR_UPD) {
+ /* Initialize event signalling an update resource is free */
+ init_completion(&fb_data->update_res_free);
+
+ fb_data->waiting_for_wb = true;
+
+ /* Leave spinlock while waiting for WB to complete */
+ mutex_unlock(&fb_data->queue_mutex);
+ wait_for_completion(&fb_data->update_res_free);
+ mutex_lock(&fb_data->queue_mutex);
+ }
+
+ if (fb_data->submit_upd_sta != SPDC_CONCUR_UPD)
+ fb_data->cur_update = upd_data_list;
+
+ /* program SPDC register and trigger to process buffer*/
+ fb_data->fresh_param.buf_addr.next_buf_phys_addr = update_addr;
+
+ fb_data->fresh_param.update_region.left = adj_update_region.left;
+ fb_data->fresh_param.update_region.top = adj_update_region.top;
+ fb_data->fresh_param.update_region.width = adj_update_region.width;
+ fb_data->fresh_param.update_region.height = adj_update_region.height;
+ fb_data->fresh_param.temper = upd_data_list->update_desc->upd_data.temp;
+ fb_data->fresh_param.wave_mode =
+ upd_data_list->update_desc->upd_data.waveform_mode;
+
+ spdc_submit_update(fb_data);
+
+ /* Release buffer queues */
+ mutex_unlock(&fb_data->queue_mutex);
+}
+
+int mxc_spdc_fb_send_update(struct mxcfb_update_data *upd_data,
+ struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = info ?
+ (mxc_spdc_t *)info:g_fb_data;
+ struct update_data_list *upd_data_list = NULL;
+ struct mxcfb_rect *screen_upd_region; /* Region on screen to update */
+ struct update_desc_list *upd_desc;
+ struct update_marker_data *marker_data;
+ int ret;
+
+ /* Has SPDC HW been initialized? */
+ if (!fb_data->hw_ready) {
+ /* Throw message if we are not mid-initialization */
+ if (!fb_data->hw_initializing)
+ dev_err(fb_data->dev, "Display HW not properly"
+ "initialized. Aborting update.\n");
+ return -EPERM;
+ }
+
+ if ((upd_data->waveform_mode > SPDC_WAV_MODE_5) &&
+ (upd_data->waveform_mode != WAVEFORM_MODE_AUTO)) {
+ dev_err(fb_data->dev,
+ "Update waveform mode 0x%x is invalid."
+ " Aborting update.\n",
+ upd_data->waveform_mode);
+ return -EINVAL;
+ }
+ if ((upd_data->update_region.left + upd_data->update_region.width >
+ fb_data->spdc_fb_var.xres + 1) ||
+ (upd_data->update_region.top + upd_data->update_region.height >
+ fb_data->spdc_fb_var.yres + 1)) {
+ dev_err(fb_data->dev,
+ "Update region is outside bounds of framebuffer."
+ "Aborting update.\n");
+ return -EINVAL;
+ }
+ if (upd_data->flags & EPDC_FLAG_USE_ALT_BUFFER) {
+ if ((upd_data->update_region.width !=
+ upd_data->alt_buffer_data.alt_update_region.width) ||
+ (upd_data->update_region.height !=
+ upd_data->alt_buffer_data.alt_update_region.height)) {
+ dev_err(fb_data->dev,
+ "Alternate update region dimensions must "
+ "match screen update region dimensions.\n");
+ return -EINVAL;
+ }
+ /* Validate physical address parameter */
+ if ((upd_data->alt_buffer_data.phys_addr <
+ fb_data->info.fix.smem_start) ||
+ (upd_data->alt_buffer_data.phys_addr >
+ fb_data->info.fix.smem_start + fb_data->map_size)) {
+ dev_err(fb_data->dev,
+ "Invalid physical address for alternate "
+ "buffer. Aborting update...\n");
+ return -EINVAL;
+ }
+ }
+
+ mutex_lock(&fb_data->queue_mutex);
+
+ /*
+ * If we are waiting to go into suspend, or the FB is blanked,
+ * we do not accept new updates
+ */
+ if (fb_data->waiting_for_idle) {
+ dev_dbg(fb_data->dev, "SPDC not active."
+ "Update request abort.\n");
+ mutex_unlock(&fb_data->queue_mutex);
+ return -EPERM;
+ }
+
+ if (fb_data->upd_scheme == UPDATE_SCHEME_SNAPSHOT) {
+ int count = 0;
+ struct update_data_list *plist;
+
+ /* Count buffers in free buffer list */
+ list_for_each_entry(plist, &fb_data->upd_buf_free_list, list)
+ count++;
+
+ /* Use count to determine if we have enough
+ * free buffers to handle this update request */
+ if (count + fb_data->max_num_buffers
+ <= fb_data->max_num_updates) {
+ dev_err(fb_data->dev,
+ "No free intermediate buffers available.\n");
+ mutex_unlock(&fb_data->queue_mutex);
+ return -ENOMEM;
+ }
+
+ /* Grab first available buffer and delete from the free list */
+ upd_data_list =
+ list_entry(fb_data->upd_buf_free_list.next,
+ struct update_data_list, list);
+
+ list_del_init(&upd_data_list->list);
+ }
+
+ /*
+ * Create new update data structure, fill it with new update
+ * data and add it to the list of pending updates
+ */
+ upd_desc = kzalloc(sizeof(struct update_desc_list), GFP_KERNEL);
+ if (!upd_desc) {
+ dev_err(fb_data->dev,
+ "Insufficient system memory for update! Aborting.\n");
+ if (fb_data->upd_scheme == UPDATE_SCHEME_SNAPSHOT) {
+ list_add(&upd_data_list->list,
+ &fb_data->upd_buf_free_list);
+ }
+ mutex_unlock(&fb_data->queue_mutex);
+ return -EPERM;
+ }
+ /* Initialize per-update marker list */
+ INIT_LIST_HEAD(&upd_desc->upd_marker_list);
+ upd_desc->upd_data = *upd_data;
+ list_add_tail(&upd_desc->list, &fb_data->upd_pending_list);
+
+ /* If marker specified, associate it with a completion */
+ if (upd_data->update_marker != 0) {
+
+ /* Allocate new update marker and set it up */
+ marker_data = kzalloc(sizeof(struct update_marker_data),
+ GFP_KERNEL);
+ if (!marker_data) {
+ dev_err(fb_data->dev, "No memory for marker!\n");
+ mutex_unlock(&fb_data->queue_mutex);
+ return -ENOMEM;
+ }
+ list_add_tail(&marker_data->upd_list,
+ &upd_desc->upd_marker_list);
+ marker_data->update_marker = upd_data->update_marker;
+ init_completion(&marker_data->update_completion);
+
+ /* Add marker to master marker list */
+ list_add_tail(&marker_data->full_list,
+ &fb_data->full_marker_list);
+ }
+
+ if (fb_data->upd_scheme != UPDATE_SCHEME_SNAPSHOT) {
+ /* Queued update scheme processing */
+
+ mutex_unlock(&fb_data->queue_mutex);
+
+ /* Signal workqueue to handle new update */
+ queue_work(fb_data->spdc_submit_workqueue,
+ &fb_data->spdc_submit_work);
+
+ return 0;
+ }
+
+ /* Set descriptor for current update, delete from pending list */
+ upd_data_list->update_desc = upd_desc;
+ list_del_init(&upd_desc->list);
+
+ mutex_unlock(&fb_data->queue_mutex);
+
+ /*
+ * Hold on to original screen update region, which we
+ * will ultimately use when telling SPDC where to update on panel
+ */
+ screen_upd_region = &upd_desc->upd_data.update_region;
+
+ /* Select from PxP output buffers */
+ upd_data_list->phys_addr =
+ fb_data->phys_addr_updbuf[fb_data->upd_buffer_num];
+ upd_data_list->virt_addr =
+ fb_data->virt_addr_updbuf[fb_data->upd_buffer_num];
+ fb_data->upd_buffer_num++;
+ if (fb_data->upd_buffer_num > fb_data->max_num_buffers-1)
+ fb_data->upd_buffer_num = 0;
+
+ ret = spdc_process_update(upd_data_list, fb_data);
+ if (ret) {
+ mutex_unlock(&fb_data->pxp_mutex);
+ return ret;
+ }
+
+ /* Pass selected waveform mode back to user */
+ upd_data->waveform_mode = upd_desc->upd_data.waveform_mode;
+
+ /* Get rotation-adjusted coordinates */
+ adjust_coordinates(fb_data->spdc_fb_var.xres,
+ fb_data->spdc_fb_var.yres, fb_data->spdc_fb_var.rotate,
+ &upd_desc->upd_data.update_region, NULL);
+
+ /* Grab lock for queue manipulation and update submission */
+ mutex_lock(&fb_data->queue_mutex);
+
+ /*
+ * Is the working buffer idle?
+ * If either the working buffer is busy, or there are no LUTs available,
+ * then we return and let the ISR handle the update later
+ */
+ if (fb_data->cur_update != NULL) {
+
+ /* Add processed Y buffer to update list */
+ list_add_tail(&upd_data_list->list, &fb_data->upd_buf_queue);
+
+ /* Return and allow the update to be submitted by the ISR. */
+ mutex_unlock(&fb_data->queue_mutex);
+ return 0;
+ }
+
+ /* Save current update */
+ fb_data->cur_update = upd_data_list;
+
+ /* program SPDC register and trigger to process buffer*/
+ fb_data->fresh_param.buf_addr.next_buf_phys_addr =
+ upd_data_list->phys_addr + (upd_data_list->update_desc->spdc_offs / 2);
+
+ fb_data->fresh_param.update_region.left = screen_upd_region->left;
+ fb_data->fresh_param.update_region.top = screen_upd_region->top;
+ fb_data->fresh_param.update_region.width = screen_upd_region->width;
+ fb_data->fresh_param.update_region.height = screen_upd_region->height;
+ fb_data->fresh_param.temper = upd_desc->upd_data.temp;
+ fb_data->fresh_param.wave_mode = upd_desc->upd_data.waveform_mode;
+ spdc_submit_update(fb_data);
+
+ mutex_unlock(&fb_data->queue_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(mxc_spdc_fb_send_update);
+
+/*
+ * return 0 : spdc update is update
+ */
+int
+mxc_spdc_fb_wait_update_complete(struct mxcfb_update_marker_data *marker_data,
+ struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = info ?
+ (mxc_spdc_t *)info:g_fb_data;
+ struct update_marker_data *next_marker;
+ struct update_marker_data *temp;
+ bool marker_found = false;
+ int ret = 0;
+
+ /* 0 is an invalid update_marker value */
+ if (marker_data->update_marker == 0)
+ return -EINVAL;
+
+ /*
+ * Find completion associated with update_marker requested.
+ * Note: If update completed already, marker will have been
+ * cleared, it won't be found, and function will just return.
+ */
+
+ /* Grab queue lock to protect access to marker list */
+ mutex_lock(&fb_data->queue_mutex);
+
+ list_for_each_entry_safe(next_marker, temp,
+ &fb_data->full_marker_list, full_list) {
+ if (next_marker->update_marker == marker_data->update_marker) {
+ dev_dbg(fb_data->dev, "Waiting for marker %d\n",
+ marker_data->update_marker);
+ next_marker->waiting = true;
+ marker_found = true;
+ break;
+ }
+ }
+
+ mutex_unlock(&fb_data->queue_mutex);
+
+ /*
+ * If marker not found, it has either been signalled already
+ * or the update request failed. In either case, just return.
+ */
+ if (!marker_found)
+ return ret;
+
+ ret = wait_for_completion_timeout(&next_marker->update_completion,
+ msecs_to_jiffies(8000));
+ if (!ret) {
+ dev_err(fb_data->dev,
+ "Timed out waiting for update completion\n");
+ return -ETIMEDOUT;
+ }
+
+ /** since SPDC don't support auto collision detect,
+ * there alway returns no collision
+ */
+ marker_data->collision_test = false;
+
+ /* Free update marker object */
+ kfree(next_marker);
+
+ return ret;
+}
+EXPORT_SYMBOL(mxc_spdc_fb_wait_update_complete);
+
+int mxc_spdc_fb_set_pwrdown_delay(u32 pwrdown_delay,
+ struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = info ?
+ (mxc_spdc_t *)info:g_fb_data;
+
+ fb_data->pwrdown_delay = pwrdown_delay;
+
+ return 0;
+}
+EXPORT_SYMBOL(mxc_spdc_fb_set_pwrdown_delay);
+
+int mxc_spdc_get_pwrdown_delay(struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = info ?
+ (mxc_spdc_t *)info:g_fb_data;
+
+ return fb_data->pwrdown_delay;
+}
+EXPORT_SYMBOL(mxc_spdc_get_pwrdown_delay);
+
+static int
+mxc_spdc_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
+{
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ void __user *argp = (void __user *)arg;
+ int ret = -EINVAL;
+
+ dev_dbg(fb_data->dev, "cmd = %08X, arg = %08X\n", cmd, (u32)arg);
+
+ switch (cmd) {
+ case MXCFB_SET_WAVEFORM_MODES:
+ {
+ struct mxcfb_waveform_modes modes;
+ if (!copy_from_user(&modes, argp, sizeof(modes))) {
+ mxc_spdc_fb_set_waveform_modes(&modes, info);
+ ret = 0;
+ }
+ break;
+ }
+ case MXCFB_SET_TEMPERATURE:
+ {
+ int temperature;
+ if (!get_user(temperature, (int32_t __user *) arg))
+ ret = mxc_spdc_fb_set_temperature(temperature,
+ info);
+ break;
+ }
+ case MXCFB_SET_AUTO_UPDATE_MODE:
+ {
+ u32 auto_mode = 0;
+ if (!get_user(auto_mode, (__u32 __user *) arg))
+ ret = mxc_spdc_fb_set_auto_update(auto_mode,
+ info);
+ break;
+ }
+ case MXCFB_SET_UPDATE_SCHEME:
+ {
+ u32 upd_scheme = 0;
+ if (!get_user(upd_scheme, (__u32 __user *) arg))
+ ret = mxc_spdc_fb_set_upd_scheme(upd_scheme,
+ info);
+ break;
+ }
+ case MXCFB_SEND_UPDATE:
+ {
+ struct mxcfb_update_data upd_data;
+ if (!copy_from_user(&upd_data, argp,
+ sizeof(upd_data))) {
+ ret = mxc_spdc_fb_send_update(&upd_data, info);
+ if (ret == 0 && copy_to_user(argp, &upd_data,
+ sizeof(upd_data)))
+ ret = -EFAULT;
+ } else {
+ ret = -EFAULT;
+ }
+
+ break;
+ }
+ case MXCFB_WAIT_FOR_UPDATE_COMPLETE:
+ {
+ struct mxcfb_update_marker_data upd_marker_data;
+ if (!copy_from_user(&upd_marker_data, argp,
+ sizeof(upd_marker_data))) {
+ ret = mxc_spdc_fb_wait_update_complete(
+ &upd_marker_data, info);
+ if (copy_to_user(argp, &upd_marker_data,
+ sizeof(upd_marker_data)))
+ ret = -EFAULT;
+ } else {
+ ret = -EFAULT;
+ }
+
+ break;
+ }
+ case MXCFB_SET_PWRDOWN_DELAY:
+ {
+ int delay = 0;
+ if (!get_user(delay, (__u32 __user *) arg))
+ ret =
+ mxc_spdc_fb_set_pwrdown_delay(delay, info);
+ break;
+ }
+ case MXCFB_GET_PWRDOWN_DELAY:
+ {
+ int pwrdown_delay = mxc_spdc_get_pwrdown_delay(info);
+ if (put_user(pwrdown_delay, (int __user *)arg))
+ ret = -EFAULT;
+ break;
+ }
+ default:
+ dev_err(fb_data->dev, "IOCTL_CMD: not such command\n");
+ return -ENOTTY;
+ }
+
+ return ret;
+}
+
+static void mxc_spdc_fb_update_pages(mxc_spdc_t *fb_data, u16 y1, u16 y2)
+{
+ struct mxcfb_update_data update;
+
+ /* Do partial screen update, Update full horizontal lines */
+ update.update_region.left = 0;
+ update.update_region.width = fb_data->spdc_fb_var.xres;
+ update.update_region.top = y1;
+ update.update_region.height = y2 - y1;
+ update.waveform_mode = WAVEFORM_MODE_AUTO;
+ update.update_mode = UPDATE_MODE_FULL;
+ update.update_marker = 0;
+ update.temp = SPDC_DEFAULT_TEMP;
+ update.flags = 0;
+
+ mxc_spdc_fb_send_update(&update, &fb_data->info);
+}
+
+/* this is called back from the deferred io workqueue */
+static void mxc_spdc_fb_deferred_io(struct fb_info *info,
+ struct list_head *pagelist)
+{
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ struct page *page;
+ unsigned long beg, end;
+ int y1, y2, miny, maxy;
+
+ if (fb_data->auto_mode != AUTO_UPDATE_MODE_AUTOMATIC_MODE)
+ return;
+
+ miny = INT_MAX;
+ maxy = 0;
+ list_for_each_entry(page, pagelist, lru) {
+ beg = page->index << PAGE_SHIFT;
+ end = beg + PAGE_SIZE - 1;
+ y1 = beg / info->fix.line_length;
+ y2 = end / info->fix.line_length;
+ if (y2 >= fb_data->spdc_fb_var.yres)
+ y2 = fb_data->spdc_fb_var.yres - 1;
+ if (miny > y1)
+ miny = y1;
+ if (maxy < y2)
+ maxy = y2;
+ }
+
+ mxc_spdc_fb_update_pages(fb_data, miny, maxy);
+}
+
+static int mxc_spdc_fb_blank(int blank, struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ int ret = 0;
+
+ dev_dbg(fb_data->dev, "blank = %d\n", blank);
+
+ if (fb_data->blank == blank)
+ return 0;
+
+ fb_data->blank = blank;
+ switch (blank) {
+ case FB_BLANK_POWERDOWN:
+ mxc_spdc_fb_flush_updates(fb_data);
+ /* Wait for powerdown */
+ mutex_lock(&fb_data->power_mutex);
+ if ((fb_data->power_state == POWER_STATE_ON) &&
+ (fb_data->pwrdown_delay == FB_POWERDOWN_DISABLE)) {
+
+ /* Powerdown disabled, so we disable SPDC manually */
+ int count = 0;
+ int sleep_ms = 10;
+
+ mutex_unlock(&fb_data->power_mutex);
+
+ /* If any active updates, wait for them to complete */
+ while (fb_data->updates_active) {
+ /* Timeout after 1 sec */
+ if ((count * sleep_ms) > 1000)
+ break;
+ msleep(sleep_ms);
+ count++;
+ }
+
+ fb_data->powering_down = true;
+ spdc_powerdown(fb_data);
+ } else if (fb_data->power_state != POWER_STATE_OFF) {
+ fb_data->wait_for_powerdown = true;
+ init_completion(&fb_data->powerdown_compl);
+ mutex_unlock(&fb_data->power_mutex);
+ ret =
+ wait_for_completion_timeout(&fb_data->powerdown_compl,
+ msecs_to_jiffies(5000));
+ if (!ret) {
+ dev_err(fb_data->dev,
+ "No powerdown received!\n");
+ return -ETIMEDOUT;
+ }
+ } else
+ mutex_unlock(&fb_data->power_mutex);
+ break;
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_NORMAL:
+ mxc_spdc_fb_flush_updates(fb_data);
+ break;
+ }
+
+ return ret;
+}
+
+static int mxc_spdc_fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = info ?
+ (mxc_spdc_t *)info:g_fb_data;
+ u_int y_bottom;
+
+ dev_dbg(info->device, "%s: var->yoffset %d, info->var.yoffset %d\n",
+ __func__, var->yoffset, info->var.yoffset);
+ /* check if var is valid; also, xpan is not supported */
+ if (!var || (var->xoffset != info->var.xoffset) ||
+ (var->yoffset + var->yres > var->yres_virtual)) {
+ dev_dbg(info->device, "x panning not supported\n");
+ return -EINVAL;
+ }
+
+ if ((fb_data->spdc_fb_var.xoffset == var->xoffset) &&
+ (fb_data->spdc_fb_var.yoffset == var->yoffset))
+ return 0; /* No change, do nothing */
+
+ y_bottom = var->yoffset;
+
+ if (!(var->vmode & FB_VMODE_YWRAP))
+ y_bottom += var->yres;
+
+ if (y_bottom > info->var.yres_virtual)
+ return -EINVAL;
+
+ mutex_lock(&fb_data->queue_mutex);
+
+ fb_data->fb_offset = (var->yoffset * var->xres_virtual + var->xoffset)
+ * (var->bits_per_pixel) / 8;
+
+ fb_data->spdc_fb_var.xoffset = var->xoffset;
+ fb_data->spdc_fb_var.yoffset = var->yoffset;
+
+ if (var->vmode & FB_VMODE_YWRAP)
+ info->var.vmode |= FB_VMODE_YWRAP;
+ else
+ info->var.vmode &= ~FB_VMODE_YWRAP;
+
+ mutex_unlock(&fb_data->queue_mutex);
+
+ return 0;
+}
+
+static struct fb_ops mxc_spdc_fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = mxc_spdc_fb_check_var,
+ .fb_set_par = mxc_spdc_fb_set_par,
+ .fb_setcmap = mxc_spdc_fb_setcmap,
+ .fb_setcolreg = mxc_spdc_fb_setcolreg,
+ .fb_pan_display = mxc_spdc_fb_pan_display,
+ .fb_ioctl = mxc_spdc_fb_ioctl,
+ .fb_mmap = mxc_spdc_fb_mmap,
+ .fb_blank = mxc_spdc_fb_blank,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+static struct fb_deferred_io mxc_spdc_fb_defio = {
+ .delay = HZ,
+ .deferred_io = mxc_spdc_fb_deferred_io,
+};
+
+static void spdc_done_work_func(struct work_struct *work)
+{
+ mxc_spdc_t *fb_data =
+ container_of(work, mxc_spdc_t, spdc_done_work.work);
+ spdc_powerdown(fb_data);
+}
+
+static irqreturn_t mxc_spdc_irq_handler(int irq, void *dev_id)
+{
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)dev_id;
+ ulong flags;
+ u32 int_events;
+ irqreturn_t ret = IRQ_NONE;
+
+ spin_lock_irqsave(&fb_data->lock, flags);
+
+ int_events = spdc_get_intr_stat(fb_data);
+ dev_dbg(fb_data->dev, "spdc int:%x\n", int_events);
+
+ if (int_events & SPDC_IRQ_STA_ERR) {
+ ret = IRQ_HANDLED;
+ spdc_intr_stat_clear(fb_data, SPDC_IRQ_STA_ERR);
+ dev_err(fb_data->dev, "Error IRQ\n");
+ return ret;
+ }
+
+ /*
+ * If we just completed one-time panel init, bypass
+ * queue handling, clear interrupt and return
+ */
+ if (fb_data->operation_mode &&
+ (int_events & SPDC_IRQ_STA_FRAME_UPDATE)) {
+ mutex_lock(&fb_data->queue_mutex);
+ fb_data->updates_active = false;
+ complete(&fb_data->update_finish);
+ spdc_intr_stat_clear(fb_data, SPDC_IRQ_STA_FRAME_UPDATE);
+
+ if ((fb_data->operation_mode == SPDC_DEEP_REFRESH) ||
+ fb_data->is_deep_fresh)
+ fb_data->is_deep_fresh = false;
+ else
+ fb_data->operation_mode = SPDC_NO_OPERATION;
+
+ mutex_unlock(&fb_data->queue_mutex);
+
+ return IRQ_HANDLED;
+ }
+
+ /* waveform loading to SPDC from memory */
+ if (int_events & SPDC_IRQ_STA_LUT_DOWNLOAD) {
+ if (is_lut_checksum_ok(fb_data)) {
+ complete(&fb_data->lut_down);
+ spdc_intr_stat_clear(fb_data,
+ SPDC_IRQ_STA_LUT_DOWNLOAD);
+ } else
+ dev_dbg(fb_data->dev, "Lut checksum is err!\n");
+
+ return IRQ_HANDLED;
+ }
+
+ /* SPDC init setting IRQ */
+ if (int_events & SPDC_IRQ_STA_TCON_INIT) {
+ complete(&fb_data->init_finish);
+ spdc_intr_stat_clear(fb_data, SPDC_IRQ_STA_TCON_INIT);
+
+ return IRQ_HANDLED;
+ }
+
+ spin_unlock_irqrestore(&fb_data->lock, flags);
+
+ if (spdc_is_update_finish(fb_data)) {
+ /* clear interrupt status */
+ spdc_intr_stat_clear(fb_data, SPDC_IRQ_STA_FRAME_UPDATE);
+ queue_work(fb_data->spdc_intr_workqueue,
+ &fb_data->spdc_intr_work);
+ }
+
+ return ret;
+}
+
+static void spdc_intr_work_func(struct work_struct *work)
+{
+ mxc_spdc_t *fb_data =
+ container_of(work, mxc_spdc_t, spdc_intr_work);
+ struct mxcfb_rect *next_upd_region;
+ struct update_marker_data *next_marker;
+ struct update_marker_data *temp;
+ struct update_data_list *next_upd, *temp_upd;
+
+ /* Protect access to buffer queues and to update HW */
+ mutex_lock(&fb_data->queue_mutex);
+
+ /* Check to see if all updates have completed */
+ if (list_empty(&fb_data->upd_pending_list) &&
+ is_free_list_full(fb_data) &&
+ (fb_data->cur_update == NULL)) {
+
+ fb_data->updates_active = false;
+
+ if (fb_data->pwrdown_delay != FB_POWERDOWN_DISABLE) {
+ /*
+ * Set variable to prevent overlapping
+ * enable/disable requests
+ */
+ fb_data->powering_down = true;
+
+ /* Schedule task to disable SPDC HW until next update */
+ schedule_delayed_work(&fb_data->spdc_done_work,
+ msecs_to_jiffies(fb_data->pwrdown_delay));
+
+ /* Reset counter to reduce chance of overflow */
+ fb_data->order_cnt = 0;
+ }
+
+ if (fb_data->waiting_for_idle)
+ complete(&fb_data->updates_done);
+ }
+
+ if (mxc_spdc_device_is_busy(fb_data)) {
+ /* Can't submit another update until SPDC is idle */
+ mutex_unlock(&fb_data->queue_mutex);
+ return;
+ }
+
+ /*
+ * Were we waiting on working buffer?
+ * If so, update queues and check for collisions
+ */
+ if (fb_data->cur_update != NULL) {
+ list_for_each_entry_safe(next_marker, temp,
+ &fb_data->cur_update->update_desc->upd_marker_list,
+ upd_list) {
+
+ /* Del from per-update & full list */
+ list_del_init(&next_marker->upd_list);
+ list_del_init(&next_marker->full_list);
+
+ /* Signal completion of update */
+ dev_dbg(fb_data->dev,
+ "Signaling marker %d\n",
+ next_marker->update_marker);
+ if (next_marker->waiting)
+ complete(&next_marker->update_completion);
+ else
+ kfree(next_marker);
+ }
+
+ /* Free marker list and update descriptor */
+ kfree(fb_data->cur_update->update_desc);
+
+ /* Add to free buffer list */
+ list_add_tail(&fb_data->cur_update->list,
+ &fb_data->upd_buf_free_list);
+
+ if (fb_data->submit_upd_sta & SPDC_CONCUR_UPD) {
+ fb_data->upd_preprocess_num = 0;
+ fb_data->submit_upd_sta &= (~SPDC_CONCUR_UPD);
+ }
+
+ /* free ACC update list */
+ list_for_each_entry_safe(next_upd, temp_upd,
+ &fb_data->upd_buf_preprocess_list, list) {
+ next_marker = list_entry(
+ (&next_upd->update_desc->upd_marker_list)->next,
+ struct update_marker_data, upd_list);
+
+ list_del_init(&next_marker->upd_list);
+ list_del_init(&next_marker->full_list);
+
+ /* Signal completion of update */
+ dev_dbg(fb_data->dev,
+ "Signaling marker %d\n",
+ next_marker->update_marker);
+
+ if (next_marker->waiting)
+ complete(&next_marker->update_completion);
+ else
+ kfree(next_marker);
+
+
+ list_del_init(&next_upd->list);
+ /* Add to free buffer list */
+ list_add_tail(&next_upd->list,
+ &fb_data->upd_buf_free_list);
+ kfree(next_upd->update_desc);
+ }
+
+ /* Check to see if all updates have completed */
+ if (list_empty(&fb_data->upd_pending_list) &&
+ is_free_list_full(fb_data)) {
+
+ fb_data->updates_active = false;
+
+ if (fb_data->pwrdown_delay !=
+ FB_POWERDOWN_DISABLE) {
+ /*
+ * Set variable to prevent overlapping
+ * enable/disable requests
+ */
+ fb_data->powering_down = true;
+
+ /* Schedule SPDC disable */
+ schedule_delayed_work(&fb_data->spdc_done_work,
+ msecs_to_jiffies(fb_data->pwrdown_delay));
+
+ /* Reset counter to reduce chance of overflow */
+ fb_data->order_cnt = 0;
+ }
+
+ if (fb_data->waiting_for_idle)
+ complete(&fb_data->updates_done);
+ }
+
+ /* Signal completion if submit workqueue was waiting on WB */
+ if (fb_data->waiting_for_wb) {
+ dev_dbg(fb_data->dev, "free update_res_free\n");
+ complete(&fb_data->update_res_free);
+ fb_data->waiting_for_wb = false;
+ }
+
+ /* Clear current update */
+ fb_data->cur_update = NULL;
+ }
+
+ if (fb_data->upd_scheme != UPDATE_SCHEME_SNAPSHOT) {
+ /* Queued update scheme processing */
+ /* Schedule task to submit collision and pending update */
+ if (!fb_data->powering_down)
+ queue_work(fb_data->spdc_submit_workqueue,
+ &fb_data->spdc_submit_work);
+
+ /* Release buffer queues */
+ mutex_unlock(&fb_data->queue_mutex);
+ return;
+ }
+
+ /* Snapshot update scheme processing */
+ if (list_empty(&fb_data->upd_buf_queue)) {
+ dev_dbg(fb_data->dev, "No pending updates.\n");
+
+ /* No updates pending, so we are done */
+ mutex_unlock(&fb_data->queue_mutex);
+ return;
+ } else {
+ dev_dbg(fb_data->dev, "Found a pending update!\n");
+
+ /* Process next item in update list */
+ fb_data->cur_update =
+ list_entry(fb_data->upd_buf_queue.next,
+ struct update_data_list, list);
+ list_del_init(&fb_data->cur_update->list);
+ }
+
+ /* program SPDC register and trigger to process buffer*/
+ next_upd_region =
+ &fb_data->cur_update->update_desc->upd_data.update_region;
+ fb_data->fresh_param.buf_addr.next_buf_phys_addr =
+ fb_data->cur_update->phys_addr +
+ (fb_data->cur_update->update_desc->spdc_offs / 2);
+
+ fb_data->fresh_param.update_region.left = next_upd_region->left;
+ fb_data->fresh_param.update_region.top = next_upd_region->top;
+ fb_data->fresh_param.update_region.width = next_upd_region->width;
+ fb_data->fresh_param.update_region.height = next_upd_region->height;
+ fb_data->fresh_param.temper =
+ fb_data->cur_update->update_desc->upd_data.temp;
+ fb_data->fresh_param.wave_mode =
+ fb_data->cur_update->update_desc->upd_data.waveform_mode;
+ spdc_submit_update(fb_data);
+
+ mutex_unlock(&fb_data->queue_mutex);
+
+ return;
+}
+
+/*
+ * Sysfs functions
+ */
+static ssize_t show_update(struct device *device,
+ struct device_attribute *attr, char *buf) {
+ struct fb_info *info = dev_get_drvdata(device);
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+
+ return sprintf(buf, "mode%d\n", fb_data->fresh_param.wave_mode);
+}
+
+static ssize_t store_update(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mxcfb_update_data update;
+ struct fb_info *info = dev_get_drvdata(device);
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ if (strncmp(buf, "direct", 6) == 0)
+ update.waveform_mode = fb_data->wv_modes.mode_du;
+ else if (strncmp(buf, "gc16", 4) == 0)
+ update.waveform_mode = fb_data->wv_modes.mode_gc16;
+ else if (strncmp(buf, "gc4", 3) == 0)
+ update.waveform_mode = fb_data->wv_modes.mode_gc4;
+ else if (strncmp(buf, "init", 4) == 0)
+ update.waveform_mode = fb_data->wv_modes.mode_init;
+ else if (strncmp(buf, "gu", 2) == 0)
+ update.waveform_mode = SPDC_WAV_MODE_3;
+ else if (strncmp(buf, "auto", 4) == 0)
+ update.waveform_mode = WAVEFORM_MODE_AUTO;
+
+ /* Now, request full screen update */
+ update.update_region.left = 0;
+ update.update_region.width = fb_data->spdc_fb_var.xres;
+ update.update_region.top = 0;
+ update.update_region.height = fb_data->spdc_fb_var.yres;
+ update.update_mode = UPDATE_MODE_FULL;
+ update.temp = SPDC_DEFAULT_TEMP;
+ update.update_marker = 0;
+ update.flags = 0;
+ dev_dbg(fb_data->dev, "rotation:%d, gray:%d\n",
+ fb_data->spdc_fb_var.rotate, fb_data->spdc_fb_var.grayscale);
+
+ mxc_spdc_fb_send_update(&update, info);
+
+ return count;
+}
+
+static ssize_t fresh_show(struct device *device, struct device_attribute *attr,
+ char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(device);
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+
+ return sprintf(buf, "%d\n", fb_data->operation_mode);
+}
+
+static ssize_t fresh_store(struct device *device, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *info = dev_get_drvdata(device);
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ int ret, operation;
+
+ ret = kstrtoint(buf, 10, &operation);
+ if (ret)
+ return ret;
+
+ if (operation == SPDC_DEEP_REFRESH)
+ fb_data->is_deep_fresh = true;
+ fb_data->operation_mode = operation;
+ if (operation > SPDC_NO_OPERATION &&
+ operation < SPDC_FULL_REFRESH)
+ mxc_operaton_update(fb_data);
+ else
+ mxc_spdc_refresh_display(fb_data);
+
+ return count;
+}
+
+static ssize_t temp_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(device);
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ int temp;
+
+ temp = fb_data->fresh_param.temper >> 1;
+ return sprintf(buf, "%d\n", temp);
+}
+
+static ssize_t initset_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(device);
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ u32 init_val;
+
+ get_panel_init_set(&fb_data->panel_set, &init_val);
+ return sprintf(buf, "%x\n", init_val);
+}
+
+static ssize_t concurrency_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(device);
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ bool temp;
+
+ temp = (fb_data->fresh_param.concur ? 1 : 0);
+ return sprintf(buf, "%d\n", temp);
+}
+
+static ssize_t concurrency_update(struct device *device,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct fb_info *info = dev_get_drvdata(device);
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ int ret, concur;
+
+ ret = kstrtoint(buf, 10, &concur);
+ if (ret)
+ return ret;
+
+ if (fb_data->fresh_param.concur != concur) {
+ fb_data->fresh_param.concur = concur;
+ spdc_set_update_concurrency(fb_data);
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(store_update, 0644, show_update, store_update);
+static DEVICE_ATTR(fresh, 0644, fresh_show, fresh_store);
+static DEVICE_ATTR(temp, 0644, temp_show, NULL);
+static DEVICE_ATTR(initset, 0644, initset_show, NULL);
+static DEVICE_ATTR(concurrency, 0644, concurrency_show, concurrency_update);
+
+static struct attribute *spdc_attributes[] = {
+ &dev_attr_store_update.attr,
+ &dev_attr_fresh.attr,
+ &dev_attr_temp.attr,
+ &dev_attr_initset.attr,
+ &dev_attr_concurrency.attr,
+ NULL
+};
+
+static const struct attribute_group spdc_attr_group = {
+ .attrs = spdc_attributes,
+};
+
+static int __devinit mxc_spdc_fb_probe(struct platform_device *pdev)
+{
+ struct fb_info *info;
+ mxc_spdc_t *fb_data;
+ struct resource *res, *mem;
+ struct fb_videomode *vmode;
+ int xres_virt, yres_virt, buf_size;
+ int xres_virt_rot, yres_virt_rot, pix_size_rot;
+ struct fb_var_screeninfo *var_info;
+ struct fb_fix_screeninfo *fix_info;
+ struct pxp_config_data *pxp_conf;
+ struct pxp_proc_data *proc_data;
+ struct scatterlist *sg;
+ struct update_data_list *upd_list;
+ struct update_data_list *plist, *temp_list;
+ char *options, *opt;
+ char *panel_str = NULL;
+ char name[] = "mxcspdcfb";
+ unsigned long x_mem_size = 0;
+ u32 i, cmap_size;
+ int ret = 0;
+
+ fb_data = (mxc_spdc_t *)framebuffer_alloc(sizeof(mxc_spdc_t),
+ &pdev->dev);
+ if (!fb_data) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ info = &fb_data->info;
+ fb_data->dev = &pdev->dev;
+ platform_set_drvdata(pdev, fb_data);
+
+ fb_data->pdata = pdev->dev.platform_data;
+ if ((fb_data->pdata == NULL) || (fb_data->pdata->num_modes < 1)
+ || (fb_data->pdata->spdc_mode == NULL)
+ || (fb_data->pdata->spdc_mode->vmode == NULL)) {
+ ret = -EINVAL;
+ goto dealloc_fb;
+ }
+
+ /*get panel para from command*/
+ if (fb_get_options(name, &options)) {
+ ret = -ENODEV;
+ goto dealloc_fb;
+ }
+ if (options)
+ while ((opt = strsep(&options, ",")) != NULL) {
+ if (!*opt)
+ continue;
+ if (!strncmp(opt, "bpp=", 4)) {
+ if (kstrtoint(opt + 4, 0,
+ &fb_data->default_bpp) < 0)
+ fb_data->default_bpp = 0;
+ } else if (!strncmp(opt, "x_mem=", 6))
+ x_mem_size = memparse(opt + 6, NULL);
+ else
+ panel_str = opt;
+ }
+
+ if (!fb_data->default_bpp)
+ fb_data->default_bpp = SPDC_DEFAULT_BPP;
+
+ /* Set default (first defined mode) for a match */
+ mxc_spdc_find_match_mode(fb_data);
+
+ if (panel_str)
+ for (i = 0; i < fb_data->pdata->num_modes; i++)
+ if (!strcmp(fb_data->pdata->spdc_mode[i].vmode->name,
+ panel_str)) {
+ fb_data->cur_mode =
+ &fb_data->pdata->spdc_mode[i];
+ break;
+ }
+ vmode = fb_data->cur_mode->vmode;
+
+ /* Allocate color map for the FB */
+ cmap_size = 256;
+ ret = fb_alloc_cmap(&info->cmap, cmap_size, 0);
+ if (ret)
+ goto dealloc_fb;
+
+ /*
+ * GPU alignment restrictions dictate framebuffer parameters:
+ * - 32-byte alignment for buffer width
+ * - 128-byte alignment for buffer height
+ * => 4K buffer alignment for buffer start
+ */
+ xres_virt = ALIGN(vmode->xres, 32);
+ yres_virt = ALIGN(vmode->yres, 128);
+ fb_data->max_pix_size = PAGE_ALIGN(xres_virt * yres_virt);
+
+ /*
+ * Have to check to see if aligned buffer size when rotated
+ * is bigger than when not rotated, and use the max
+ */
+ xres_virt_rot = ALIGN(vmode->yres, 32);
+ yres_virt_rot = ALIGN(vmode->xres, 128);
+ pix_size_rot = PAGE_ALIGN(xres_virt_rot * yres_virt_rot);
+ fb_data->max_pix_size = (fb_data->max_pix_size > pix_size_rot) ?
+ fb_data->max_pix_size : pix_size_rot;
+ buf_size = fb_data->max_pix_size * fb_data->default_bpp/8;
+
+ /* Compute the number of screens needed based on X memory requested */
+ if (x_mem_size > 0) {
+ fb_data->num_screens = DIV_ROUND_UP(x_mem_size, buf_size);
+ if (fb_data->num_screens < NUM_SCREENS_MIN)
+ fb_data->num_screens = NUM_SCREENS_MIN;
+ else if (buf_size * fb_data->num_screens > SZ_16M)
+ fb_data->num_screens = SZ_16M / buf_size;
+ } else
+ fb_data->num_screens = NUM_SCREENS_MIN;
+
+ fb_data->map_size = buf_size * fb_data->num_screens;
+ dev_dbg(&pdev->dev, "memory allocate: %d\n", fb_data->map_size);
+
+ /* get IO memory*/
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to get memory register\n");
+ ret = -ENXIO;
+ goto release_cmap;
+ }
+
+ mem = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to get memory region\n");
+ ret = -ENOENT;
+ goto release_cmap;
+ }
+
+ fb_data->hwp = ioremap(res->start, SZ_4K);
+ if (fb_data->hwp == NULL) {
+ dev_err(&pdev->dev, "ioremap registers failed\n");
+ ret = -ENOENT;
+ goto release_mem;
+ }
+
+ /* Allocate FB memory */
+ fb_data->virt_start = dma_alloc_writecombine(&pdev->dev,
+ fb_data->map_size, &fb_data->phys_start, GFP_DMA);
+ if (fb_data->virt_start == NULL) {
+ dev_err(&pdev->dev, "probe err - dma_alloc for framebuffer\n");
+ ret = -ENOMEM;
+ goto release_hwp;
+ }
+
+ var_info = &info->var;
+ var_info->activate = FB_ACTIVATE_TEST;
+ var_info->bits_per_pixel = fb_data->default_bpp;
+ var_info->xres = vmode->xres;
+ var_info->yres = vmode->yres;
+ var_info->xres_virtual = xres_virt;
+ var_info->yres_virtual = yres_virt * fb_data->num_screens;
+ var_info->pixclock = vmode->pixclock;
+ var_info->left_margin = vmode->left_margin;
+ var_info->right_margin = vmode->right_margin;
+ var_info->upper_margin = vmode->upper_margin;
+ var_info->lower_margin = vmode->lower_margin;
+ var_info->hsync_len = vmode->hsync_len;
+ var_info->vsync_len = vmode->vsync_len;
+ var_info->vmode = FB_VMODE_NONINTERLACED;
+
+ switch (fb_data->default_bpp) {
+ case 32:
+ case 24:
+ var_info->red.offset = 16;
+ var_info->red.length = 8;
+ var_info->green.offset = 8;
+ var_info->green.length = 8;
+ var_info->blue.offset = 0;
+ var_info->blue.length = 8;
+ break;
+ case 16:
+ var_info->red.offset = 11;
+ var_info->red.length = 5;
+ var_info->green.offset = 5;
+ var_info->green.length = 6;
+ var_info->blue.offset = 0;
+ var_info->blue.length = 5;
+ break;
+ case 8:
+ var_info->grayscale = GRAYSCALE_8BIT;
+
+ var_info->red.length = 8;
+ var_info->red.offset = 0;
+ var_info->red.msb_right = 0;
+ var_info->green.length = 8;
+ var_info->green.offset = 0;
+ var_info->green.msb_right = 0;
+ var_info->blue.length = 8;
+ var_info->blue.offset = 0;
+ var_info->blue.msb_right = 0;
+ break;
+ case 4:
+ var_info->grayscale = GRAYSCALE_4BIT;
+
+ var_info->red.length = 4;
+ var_info->red.offset = 0;
+ var_info->red.msb_right = 0;
+ var_info->green.length = 4;
+ var_info->green.offset = 0;
+ var_info->green.msb_right = 0;
+ var_info->blue.length = 4;
+ var_info->blue.offset = 0;
+ var_info->blue.msb_right = 0;
+ break;
+ default:
+ dev_err(&pdev->dev, "unsupported bit-width:%d\n",
+ fb_data->default_bpp);
+ ret = -EINVAL;
+ goto release_dma_fb;
+ }
+
+ fix_info = &info->fix;
+ strcpy(fix_info->id, SPDC_DRIVER_NAME);
+ fix_info->type = FB_TYPE_PACKED_PIXELS;
+ fix_info->visual = FB_VISUAL_TRUECOLOR;
+ fix_info->xpanstep = 0;
+ fix_info->ypanstep = 0;
+ fix_info->ywrapstep = 0;
+ fix_info->accel = FB_ACCEL_NONE;
+ fix_info->smem_start = fb_data->phys_start;
+ fix_info->smem_len = fb_data->map_size;
+ fix_info->ypanstep = 0;
+
+ /* Set up FB info */
+ fb_data->native_width = vmode->xres;
+ fb_data->native_height = vmode->yres;
+ info->screen_base = fb_data->virt_start;
+ info->screen_size = info->fix.smem_len;
+ info->fbops = &mxc_spdc_fb_ops;
+ info->var.activate = FB_ACTIVATE_NOW;
+ info->pseudo_palette = fb_data->pseudo_palette;
+ info->flags = FBINFO_FLAG_DEFAULT;
+
+ mxc_spdc_fb_set_fix(info);
+
+ /* use the same AXI and PIX clock source */
+ fb_data->spdc_clk_axi = clk_get(fb_data->dev, "epdc_axi");
+ if (IS_ERR(fb_data->spdc_clk_axi)) {
+ dev_err(&pdev->dev, "Unable to get AXI clk."
+ "err = 0x%x\n", (int)fb_data->spdc_clk_axi);
+ ret = -ENODEV;
+ goto release_dma_fb;
+ }
+ fb_data->spdc_clk_pix = clk_get(fb_data->dev, "epdc_pix");
+ if (IS_ERR(fb_data->spdc_clk_pix)) {
+ dev_err(&pdev->dev, "Unable to get pix clk."
+ "err = 0x%x\n", (int)fb_data->spdc_clk_pix);
+ ret = -ENODEV;
+ goto release_dma_fb;
+ }
+
+ /*
+ * Initialize update list and allocate buffer.
+ */
+ INIT_LIST_HEAD(&fb_data->upd_pending_list);
+ INIT_LIST_HEAD(&fb_data->upd_buf_queue);
+ INIT_LIST_HEAD(&fb_data->upd_buf_free_list);
+ INIT_LIST_HEAD(&fb_data->upd_buf_preprocess_list);
+ INIT_LIST_HEAD(&fb_data->full_marker_list);
+
+ fb_data->max_num_updates = SPDC_MAX_NUM_UPDATES;
+ fb_data->max_num_buffers = SPDC_MAX_NUM_BUFFERS;
+
+ /* Allocate free buffers */
+ for (i = 0; i < fb_data->max_num_updates; i++) {
+ upd_list = kzalloc(sizeof(*upd_list), GFP_KERNEL);
+ if (upd_list == NULL) {
+ ret = -ENOMEM;
+ goto release_dma_fb;
+ }
+
+ /* Add newly allocated buffer to free list */
+ list_add(&upd_list->list, &fb_data->upd_buf_free_list);
+ }
+
+ /* Allocate PXP output buffer */
+ fb_data->virt_addr_updbuf =
+ kzalloc(sizeof(void *) * fb_data->max_num_buffers, GFP_KERNEL);
+ fb_data->phys_addr_updbuf =
+ kzalloc(sizeof(dma_addr_t) * fb_data->max_num_buffers,
+ GFP_KERNEL);
+ for (i = 0; i < fb_data->max_num_buffers; i++) {
+ /*
+ * Allocate memory for PxP output buffer.
+ * Output raw data is Y4 format.
+ * Each update buffer is 1/2 byte per pixel, and can
+ * be as big as the full-screen frame buffer
+ */
+ fb_data->virt_addr_updbuf[i] =
+ dma_alloc_coherent(fb_data->info.device,
+ fb_data->max_pix_size / 2,
+ &fb_data->phys_addr_updbuf[i], GFP_DMA);
+ if (fb_data->virt_addr_updbuf[i] == NULL) {
+ ret = -ENOMEM;
+ goto release_freebuf_lists;
+ }
+ }
+ /* Counter indicating which update buffer should be used next. */
+ fb_data->upd_buffer_num = 0;
+
+ /* Allocate memory for partical process region buffer.
+ * Output raw data is Y4 format.
+ */
+ fb_data->virt_addr_copybuf =
+ dma_alloc_coherent(fb_data->info.device,
+ fb_data->max_pix_size / 2,
+ &fb_data->phys_addr_copybuf, GFP_DMA);
+ if (fb_data->virt_addr_copybuf == NULL) {
+ ret = -ENOMEM;
+ goto release_output_buf;
+ }
+
+ /* Allocate next & current & privious & count & lut buffers.
+ * next buffer size is Y4 raw data
+ */
+ fb_data->next_buf_size =
+ (fb_data->map_size / fb_data->num_screens) >> 1;
+ fb_data->virt_next_buf =
+ dma_alloc_coherent(&pdev->dev, fb_data->next_buf_size,
+ &fb_data->phy_next_buf, GFP_DMA);
+ if (fb_data->virt_next_buf == NULL) {
+ dev_err(&pdev->dev, "Can't allocate mem for next buf!\n");
+ ret = -ENOMEM;
+ goto release_copy_buf;
+ }
+
+ fb_data->current_buf_size =
+ (fb_data->map_size / fb_data->num_screens) >> 1;
+ fb_data->virt_current_buf =
+ dma_alloc_coherent(&pdev->dev, fb_data->current_buf_size,
+ &fb_data->phy_current_buf, GFP_DMA);
+ if (fb_data->virt_current_buf == NULL) {
+ dev_err(&pdev->dev, "Can't allocate mem for current buf!\n");
+ ret = -ENOMEM;
+ goto release_next_buf;
+ }
+
+ fb_data->pre_buf_size =
+ (fb_data->map_size / fb_data->num_screens) >> 1;
+ fb_data->virt_pre_buf =
+ dma_alloc_coherent(&pdev->dev, fb_data->pre_buf_size,
+ &fb_data->phy_pre_buf, GFP_DMA);
+ if (fb_data->virt_pre_buf == NULL) {
+ dev_err(&pdev->dev, "Can't allocate mem for current buf!\n");
+ ret = -ENOMEM;
+ goto release_current_buf;
+ }
+
+ fb_data->cnt_buf_size = info->var.xres * info->var.yres;
+ fb_data->virt_cnt_buf =
+ dma_alloc_coherent(&pdev->dev, fb_data->cnt_buf_size,
+ &fb_data->phy_cnt_buf, GFP_DMA);
+ if (fb_data->virt_cnt_buf == NULL) {
+ dev_err(&pdev->dev, "Can't allocate mem for current buf!\n");
+ ret = -ENOMEM;
+ goto release_pre_buf;
+ }
+
+ fb_data->lut_buf_size = SZ_1M;
+ fb_data->virt_lut_buf =
+ dma_alloc_coherent(&pdev->dev, fb_data->lut_buf_size,
+ &fb_data->phy_lut_buf, GFP_DMA);
+ if (fb_data->virt_lut_buf == NULL) {
+ dev_err(&pdev->dev, "Can't allocate mem for current buf!\n");
+ ret = -ENOMEM;
+ goto release_cnt_buf;
+ }
+
+ /* Initialize SPDC pins */
+ if (fb_data->pdata->get_pins)
+ fb_data->pdata->get_pins();
+
+ fb_data->hw_ready = false;
+ fb_data->hw_initializing = false;
+
+ /*
+ * Set default waveform mode values.
+ * Should be overwritten via ioctl.
+ */
+ fb_data->wv_modes.mode_init = SPDC_WAV_MODE_DEFAULT;
+ fb_data->wv_modes.mode_du = SPDC_WAV_MODE_4;
+ fb_data->wv_modes.mode_gc4 = SPDC_WAV_MODE_2;
+ fb_data->wv_modes.mode_gc8 = SPDC_WAV_MODE_1;
+ fb_data->wv_modes.mode_gc16 = SPDC_WAV_MODE_1;
+ fb_data->wv_modes.mode_gc32 = SPDC_WAV_MODE_1;
+
+ fb_data->auto_mode = AUTO_UPDATE_MODE_REGION_MODE;
+ fb_data->upd_scheme = UPDATE_SCHEME_QUEUE_AND_MERGE;
+ fb_data->spdc_fb_var = *var_info;
+ fb_data->fb_offset = 0;
+
+ fb_data->blank = FB_BLANK_UNBLANK;
+ fb_data->powering_down = false;
+ fb_data->power_state = POWER_STATE_OFF;
+ fb_data->pwrdown_delay = 0;
+ fb_data->cur_update = NULL;
+ fb_data->waiting_for_idle = false;
+ fb_data->order_cnt = 0;
+ fb_data->waiting_for_wb = false;
+ fb_data->wait_for_powerdown = false;
+ fb_data->updates_active = false;
+
+ spin_lock_init(&fb_data->lock);
+ mutex_init(&fb_data->queue_mutex);
+ mutex_init(&fb_data->pxp_mutex);
+ mutex_init(&fb_data->power_mutex);
+ init_completion(&fb_data->lut_down);
+ init_completion(&fb_data->init_finish);
+ init_completion(&fb_data->update_finish);
+ INIT_DELAYED_WORK(&fb_data->spdc_done_work, spdc_done_work_func);
+
+ fb_data->spdc_submit_workqueue = alloc_workqueue("SPDC Submit",
+ WQ_MEM_RECLAIM | WQ_HIGHPRI |
+ WQ_CPU_INTENSIVE | WQ_UNBOUND, 1);
+ INIT_WORK(&fb_data->spdc_submit_work, spdc_submit_work_func);
+ fb_data->spdc_intr_workqueue = alloc_workqueue("SPDC Interrupt",
+ WQ_MEM_RECLAIM | WQ_HIGHPRI |
+ WQ_CPU_INTENSIVE | WQ_UNBOUND, 1);
+ INIT_WORK(&fb_data->spdc_intr_work, spdc_intr_work_func);
+
+ /* Retrieve spdc IRQ num */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "cannot get IRQ resource\n");
+ ret = -ENODEV;
+ goto release_lut_buf;
+ }
+ fb_data->spdc_irq = res->start;
+ ret = request_irq(fb_data->spdc_irq, mxc_spdc_irq_handler, 0,
+ "fb_dma", fb_data);
+ if (ret) {
+ dev_err(&pdev->dev, "request_irq (%d) failed with error %d\n",
+ fb_data->spdc_irq, ret);
+ ret = -ENODEV;
+ goto release_lut_buf;
+ }
+
+ /* define deferred io */
+ info->fbdefio = &mxc_spdc_fb_defio;
+#ifdef CONFIG_FB_MXC_SIPIX_AUTO_UPDATE_MODE
+ fb_deferred_io_init(info);
+#endif
+
+ /* get pmic regulators */
+ fb_data->display_regulator = regulator_get(NULL, "DISPLAY");
+ if (IS_ERR(fb_data->display_regulator)) {
+ dev_err(&pdev->dev, "Unable to get display PMIC regulator."
+ "err = 0x%x\n", (int)fb_data->display_regulator);
+ ret = -ENODEV;
+ goto release_irq;
+ }
+ fb_data->vcom_regulator = regulator_get(NULL, "VCOM");
+ if (IS_ERR(fb_data->vcom_regulator)) {
+ regulator_put(fb_data->display_regulator);
+ dev_err(&pdev->dev, "Unable to get VCOM regulator."
+ "err = 0x%x\n", (int)fb_data->vcom_regulator);
+ ret = -ENODEV;
+ goto release_regulator1;
+ }
+ fb_data->v3p3_regulator = regulator_get(NULL, "V3P3");
+ if (IS_ERR(fb_data->v3p3_regulator)) {
+ regulator_put(fb_data->vcom_regulator);
+ regulator_put(fb_data->display_regulator);
+ dev_err(&pdev->dev, "Unable to get V3P3 regulator."
+ "err = 0x%x\n", (int)fb_data->vcom_regulator);
+ ret = -ENODEV;
+ goto release_regulator2;
+ }
+
+ /*
+ * Fill out PxP config data structure based on FB info and
+ * processing tasks required
+ */
+ pxp_conf = &fb_data->pxp_conf;
+ proc_data = &pxp_conf->proc_data;
+
+ /* Initialize non-channel-specific PxP parameters */
+ proc_data->drect.left = proc_data->srect.left = 0;
+ proc_data->drect.top = proc_data->srect.top = 0;
+ proc_data->drect.width = fb_data->info.var.xres;
+ proc_data->srect.width = fb_data->info.var.xres;
+ proc_data->drect.height = fb_data->info.var.yres;
+ proc_data->srect.height = fb_data->info.var.yres;
+ proc_data->scaling = 0;
+ proc_data->hflip = 0;
+ proc_data->vflip = 0;
+ proc_data->rotate = 0;
+ proc_data->bgcolor = 0;
+ proc_data->overlay_state = 0;
+ proc_data->lut_transform = PXP_LUT_NONE;
+ proc_data->lut_map = NULL;
+
+ /*
+ * We initially configure PxP for RGB->YUV conversion,
+ * and only write out Y component of the result.
+ */
+
+ /*
+ * Initialize S0 channel parameters
+ * Parameters should match FB format/width/height
+ */
+ pxp_conf->s0_param.pixel_fmt = PXP_PIX_FMT_RGB565;
+ pxp_conf->s0_param.width = fb_data->info.var.xres_virtual;
+ pxp_conf->s0_param.height = fb_data->info.var.yres;
+ pxp_conf->s0_param.color_key = -1;
+ pxp_conf->s0_param.color_key_enable = false;
+
+ /*
+ * Initialize OL0 channel parameters
+ * No overlay will be used for PxP operation
+ */
+ for (i = 0; i < 8; i++) {
+ pxp_conf->ol_param[i].combine_enable = false;
+ pxp_conf->ol_param[i].width = 0;
+ pxp_conf->ol_param[i].height = 0;
+ pxp_conf->ol_param[i].pixel_fmt = PXP_PIX_FMT_RGB565;
+ pxp_conf->ol_param[i].color_key_enable = false;
+ pxp_conf->ol_param[i].color_key = -1;
+ pxp_conf->ol_param[i].global_alpha_enable = false;
+ pxp_conf->ol_param[i].global_alpha = 0;
+ pxp_conf->ol_param[i].local_alpha_enable = false;
+ }
+
+ /*
+ * Initialize Output channel parameters
+ * Output is Y-only greyscale
+ * Output width/height will vary based on update region size
+ */
+ pxp_conf->out_param.width = fb_data->info.var.xres;
+ pxp_conf->out_param.height = fb_data->info.var.yres;
+ pxp_conf->out_param.pixel_fmt = PXP_PIX_FMT_GY04;
+ pxp_conf->out_param.stride = pxp_conf->out_param.width;
+
+ /* Initialize color map for conversion of 8-bit gray pixels */
+ fb_data->pxp_conf.proc_data.lut_map = kmalloc(256, GFP_KERNEL);
+ if (fb_data->pxp_conf.proc_data.lut_map == NULL) {
+ dev_err(&pdev->dev, "Can't allocate mem for lut map!\n");
+ ret = -ENOMEM;
+ goto release_regulator3;
+ }
+ for (i = 0; i < 256; i++)
+ fb_data->pxp_conf.proc_data.lut_map[i] = i;
+
+ fb_data->pxp_conf.proc_data.lut_map_updated = true;
+
+ /*
+ * Ensure this is set to NULL here...we will initialize pxp_chan
+ * later in our thread.
+ */
+ fb_data->pxp_chan = NULL;
+
+ /* Initialize Scatter-gather list containing 2 buffer addresses. */
+ sg = fb_data->sg;
+ sg_init_table(sg, 2);
+
+ /*
+ * For use in PxP transfers:
+ * sg[0] holds the FB buffer pointer
+ * sg[1] holds the Output buffer pointer (configured before TX request)
+ */
+ sg_dma_address(&sg[0]) = info->fix.smem_start;
+ sg_set_page(&sg[0], virt_to_page(info->screen_base),
+ info->fix.smem_len, offset_in_page(info->screen_base));
+
+ /* Register FB */
+ ret = register_framebuffer(info);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "register framebuffer failed\n");
+ goto release_lutmap;
+ }
+
+ ret = sysfs_create_group(&info->device->kobj, &spdc_attr_group);
+ if (ret)
+ dev_err(&pdev->dev, "Unable to create file from fb_attrs\n");
+
+ /* use for spdc test */
+ g_fb_data = fb_data;
+
+ /* hw init */
+ spdc_fb_dev_init(fb_data);
+
+ /*detect spdc epd disp & tcon version*/
+ get_spdc_version(fb_data);
+
+ goto out;
+
+release_lutmap:
+ kfree(fb_data->pxp_conf.proc_data.lut_map);
+release_regulator3:
+ regulator_put(fb_data->v3p3_regulator);
+release_regulator2:
+ regulator_put(fb_data->vcom_regulator);
+release_regulator1:
+ regulator_put(fb_data->display_regulator);
+release_irq:
+ free_irq(fb_data->spdc_irq, fb_data);
+release_lut_buf:
+ dma_free_coherent(&pdev->dev, fb_data->pre_buf_size,
+ fb_data->virt_lut_buf, fb_data->phy_lut_buf);
+release_cnt_buf:
+ dma_free_coherent(&pdev->dev, fb_data->cnt_buf_size,
+ fb_data->virt_cnt_buf, fb_data->phy_cnt_buf);
+release_pre_buf:
+ dma_free_coherent(&pdev->dev, fb_data->pre_buf_size,
+ fb_data->virt_pre_buf, fb_data->phy_pre_buf);
+release_current_buf:
+ dma_free_coherent(&pdev->dev, fb_data->current_buf_size,
+ fb_data->virt_current_buf, fb_data->phy_current_buf);
+release_next_buf:
+ dma_free_coherent(&pdev->dev, fb_data->next_buf_size,
+ fb_data->virt_next_buf, fb_data->phy_next_buf);
+release_copy_buf:
+ dma_free_writecombine(&pdev->dev, fb_data->max_pix_size / 2,
+ fb_data->virt_addr_copybuf, fb_data->phys_addr_copybuf);
+release_output_buf:
+ for (i = 0; i < fb_data->max_num_buffers; i++)
+ if (fb_data->virt_addr_updbuf[i] != NULL)
+ dma_free_writecombine(&pdev->dev,
+ fb_data->max_pix_size / 2, fb_data->virt_addr_updbuf[i],
+ fb_data->phys_addr_updbuf[i]);
+ if (fb_data->virt_addr_updbuf != NULL)
+ kfree(fb_data->virt_addr_updbuf);
+ if (fb_data->phys_addr_updbuf != NULL)
+ kfree(fb_data->phys_addr_updbuf);
+release_freebuf_lists:
+ list_for_each_entry_safe(plist, temp_list, &fb_data->upd_buf_free_list,
+ list) {
+ list_del(&plist->list);
+ kfree(plist);
+ }
+release_dma_fb:
+ dma_free_writecombine(&pdev->dev,
+ fb_data->map_size, fb_data->virt_start, fb_data->phys_start);
+release_hwp:
+ iounmap(fb_data->hwp);
+release_mem:
+ release_resource(mem);
+release_cmap:
+ fb_dealloc_cmap(&info->cmap);
+dealloc_fb:
+ framebuffer_release(info);
+out:
+ return ret;
+}
+
+static int mxc_spdc_fb_remove(struct platform_device *pdev)
+{
+ struct update_data_list *plist, *temp_list;
+ mxc_spdc_t *fb_data = platform_get_drvdata(pdev);
+ struct fb_info *info = &fb_data->info;
+ int i;
+
+ mxc_spdc_fb_blank(FB_BLANK_POWERDOWN, &fb_data->info);
+
+ flush_workqueue(fb_data->spdc_submit_workqueue);
+ destroy_workqueue(fb_data->spdc_submit_workqueue);
+
+ regulator_put(fb_data->display_regulator);
+ regulator_put(fb_data->vcom_regulator);
+ regulator_put(fb_data->v3p3_regulator);
+
+ for (i = 0; i < fb_data->max_num_buffers; i++)
+ if (fb_data->virt_addr_updbuf[i] != NULL)
+ dma_free_writecombine(&pdev->dev,
+ fb_data->max_pix_size,
+ fb_data->virt_addr_updbuf[i],
+ fb_data->phys_addr_updbuf[i]);
+ if (fb_data->virt_addr_updbuf != NULL)
+ kfree(fb_data->virt_addr_updbuf);
+ if (fb_data->phys_addr_updbuf != NULL)
+ kfree(fb_data->phys_addr_updbuf);
+
+ /* free output temporary buffer */
+ dma_free_writecombine(&pdev->dev, fb_data->max_pix_size / 2,
+ fb_data->virt_addr_copybuf, fb_data->phys_addr_copybuf);
+
+ list_for_each_entry_safe(plist, temp_list,
+ &fb_data->upd_buf_free_list,
+ list) {
+ list_del(&plist->list);
+ kfree(plist);
+ }
+
+#if defined(CONFIG_FB_MXC_SIPIX_AUTO_UPDATE_MODE)
+ fb_deferred_io_cleanup(&fb_data->info);
+#endif
+
+ /* free frame buffer */
+ dma_free_writecombine(&pdev->dev, fb_data->map_size,
+ info->screen_base, fb_data->phys_start);
+
+ /* free SPDC hw allocate buffer */
+ dma_free_coherent(&pdev->dev, fb_data->next_buf_size,
+ fb_data->virt_next_buf, fb_data->phy_next_buf);
+ dma_free_coherent(&pdev->dev, fb_data->current_buf_size,
+ fb_data->virt_current_buf, fb_data->phy_current_buf);
+ dma_free_coherent(&pdev->dev, fb_data->pre_buf_size,
+ fb_data->virt_pre_buf, fb_data->phy_pre_buf);
+ dma_free_coherent(&pdev->dev, fb_data->cnt_buf_size,
+ fb_data->virt_cnt_buf, fb_data->phy_cnt_buf);
+ dma_free_coherent(&pdev->dev, fb_data->pre_buf_size,
+ fb_data->virt_lut_buf, fb_data->phy_lut_buf);
+
+ sysfs_remove_group(&info->device->kobj, &spdc_attr_group);
+ unregister_framebuffer(info);
+
+ if (fb_data->pdata->put_pins)
+ fb_data->pdata->put_pins();
+
+ free_irq(fb_data->spdc_irq, fb_data);
+ iounmap(fb_data->hwp);
+
+ fb_dealloc_cmap(&info->cmap);
+
+ framebuffer_release(info);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mxc_spdc_fb_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ mxc_spdc_t *data = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = mxc_spdc_fb_blank(FB_BLANK_POWERDOWN, &data->info);
+
+ return ret;
+}
+
+static int mxc_spdc_fb_resume(struct platform_device *pdev)
+{
+ mxc_spdc_t *data = platform_get_drvdata(pdev);
+
+ mxc_spdc_fb_blank(FB_BLANK_UNBLANK, &data->info);
+ return 0;
+}
+#else
+#define mxc_spdc_fb_suspend NULL
+#define mxc_spdc_fb_resume NULL
+#endif
+
+static struct platform_driver mxc_spdc_fb_driver = {
+ .probe = mxc_spdc_fb_probe,
+ .remove = mxc_spdc_fb_remove,
+ .suspend = mxc_spdc_fb_suspend,
+ .resume = mxc_spdc_fb_resume,
+ .driver = {
+ .name = SPDC_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mxc_spdc_fb_init(void)
+{
+ return platform_driver_register(&mxc_spdc_fb_driver);
+}
+late_initcall(mxc_spdc_fb_init);
+
+static void __exit mxc_spdc_fb_exit(void)
+{
+ platform_driver_unregister(&mxc_spdc_fb_driver);
+}
+module_exit(mxc_spdc_fb_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC SPDC framebuffer driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("fb");
diff --git a/drivers/video/mxc/mxc_spdc_fb.h b/drivers/video/mxc/mxc_spdc_fb.h
new file mode 100644
index 000000000000..34373788e83e
--- /dev/null
+++ b/drivers/video/mxc/mxc_spdc_fb.h
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __MXC_SPDC_FB_H__
+#define __MXC_SPDC_FB_H__
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/dmaengine.h>
+#include <linux/pxp_dma.h>
+#include <linux/mxcfb.h>
+#include <linux/list.h>
+#include <linux/uaccess.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/regulator/driver.h>
+#include <linux/dmaengine.h>
+#include <linux/gpio.h>
+#include <linux/kthread.h>
+#include <mach/epdc.h>
+#include <mach/dma.h>
+#include <linux/platform_device.h>
+
+#include <linux/fsl_devices.h>
+#include <linux/mxcfb.h>
+
+/*************************************
+*Register addresses
+*************************************/
+#define SPDC_DISP_TRIGGER (0x000)
+#define SPDC_UPDATA_X_Y (0x004)
+#define SPDC_UPDATE_W_H (0x008)
+#define SPDC_LUT_PARA_UPDATE (0x00C)
+#define SPDC_OPERATE (0x010)
+#define SPDC_PANEL_INIT_SET (0x014)
+#define SPDC_TEMP_INFO (0x018)
+#define SPDC_NEXT_BUF (0x01C)
+#define SPDC_CURRENT_BUF (0x020)
+#define SPDC_PRE_BUF (0x024)
+#define SPDC_CNT_BUF (0x028)
+#define SPDC_LUT_BUF (0x02C)
+#define SPDC_INT_ENABLE (0x030)
+#define SPDC_INT_STA_CLR (0x034)
+#define SPDC_INIT_STA_CLR (0x038)
+#define SPDC_STATUS (0x03C)
+#define SPDC_DISP_VER (0x040)
+#define SPDC_TCON_VER (0x044)
+#define SPDC_SW_GATE_CLK (0x048)
+
+/*
+ * Register field definitions
+ */
+enum {
+ SPDC_DISP_TRIGGER_ENABLE = 0x1,
+ SPDC_DISP_TRIGGER_FLASH = 0x10,
+
+ /*SPDC clock gate*/
+ SPDC_SW_GATE_CLK_ENABLE = 0x1,
+
+ /* waveform mode mask */
+ SPDC_WAV_MODE_MASK = (0x7 << 1),
+
+ /* SPDC interrupt IRQ mask define */
+ SPDC_IRQ_FRAME_UPDATE = (0x1 << 0),
+ SPDC_IRQ_TCON_INIT = (0x1 << 1),
+ SPDC_IRQ_LUT_DOWNLOAD = (0x1 << 2),
+ SPDC_IRQ_ERR = (0x1 << 3),
+ SPDC_IRQ_ALL_MASK = 0xF,
+
+ /* SPDC interrupt status */
+ SPDC_IRQ_STA_FRAME_UPDATE = (0x1 << 0),
+ SPDC_IRQ_STA_TCON_INIT = (0x1 << 1),
+ SPDC_IRQ_STA_LUT_DOWNLOAD = (0x1 << 2),
+ SPDC_IRQ_STA_ERR = (0x1 << 3),
+ SPDC_IRQ_STA_ALL_MASK = 0xF,
+
+ /* SPDC update coordinate angle */
+ SPDC_UPDATE_W_H_MAX_SIZE = ((0x1 << 12) - 1),
+ SPDC_UPDATE_X_Y_MAX_SIZE = ((0x1 << 12) - 1),
+
+ /* SPDC TCON status */
+ SPDC_PANEL_STAUTS_BUSY = 0x1,
+ SPDC_TCON_STATUS_IDLE = (0x4 << 4),
+
+ /* SPDC EPD Operation mode */
+ SPDC_NO_OPERATION = 0,
+ SPDC_DISP_REFRESH = 1,
+ SPDC_DEEP_REFRESH = 0x2,
+ SPDC_DISP_RESET = 0x4,
+ SPDC_SW_TCON_RESET = 0x5,
+ SPDC_SW_TCON_RESET_SET = 0x80000000,
+ SPDC_FULL_REFRESH = 0x6,
+
+ /* SPDC Concurrency mechanism: ACC */
+ SPDC_LUT_MODE_OFFSET = 0xb3,
+ SPDC_LUT_ACC_MODE = 0x4,
+
+ /* SPDC waveform */
+ SPDC_WAVEFORM_LUT_OFFSET_ADDR = 0x7600,
+ SPDC_LUT_PARA_LENTH = 0x100,
+ SPDC_LUT_FROM_MEM = 1,
+ SPDC_LUT_TO_MEM = 0,
+
+ /* SPDC submit update status */
+ SPDC_CONCUR_UPD = 0x4,
+ SPDC_QUEUE_UPD = 0x8,
+ SPDC_CONCUR_QUEUE = 0xc,
+};
+
+#define SPDC_DRIVER_NAME "imx_spdc_fb"
+
+/**
+ * SPDC EPD waveform display trigger mode
+ */
+enum {
+ SPDC_WAV_MODE_0 = 0,
+ SPDC_WAV_MODE_1,
+ SPDC_WAV_MODE_2,
+ SPDC_WAV_MODE_3,
+ SPDC_WAV_MODE_4,
+ SPDC_WAV_MODE_5,
+ SPDC_WAV_MODE_DEFAULT = SPDC_WAV_MODE_0,
+};
+
+/**
+ * SPDC controller ip version
+ */
+struct mxc_epd_disp_version {
+ u8 epd_type;
+ u8 lut_ver;
+ u16 product_id;
+};
+
+struct mxc_spdc_version {
+ struct mxc_epd_disp_version disp_ver;
+ u8 tcon_ver;
+};
+
+struct spdc_buffer_addr {
+ u32 next_buf_phys_addr;
+ u32 cur_buf_phys_addr;
+ u32 pre_buf_phys_addr;
+ u32 frm_cnt_buf_phys_addr;
+ u32 lut_buf_phys_addr;
+};
+
+struct partial_refresh_param {
+ struct spdc_buffer_addr buf_addr;
+ struct mxcfb_rect update_region;
+ int temper;
+ u32 blocking;
+ u32 wave_mode;
+ u32 stride;
+ u32 flash; /* only for waveform mode7 */
+ u32 concur; /* Concurrency mechanism: ACC */
+};
+
+/**
+ * SPDC lut data
+ */
+struct mxc_spdc_lut_para {
+ u8 lut_data[SPDC_LUT_PARA_LENTH];
+ u8 lut_addr[SPDC_LUT_PARA_LENTH];
+ bool lut_para_update_sta;
+};
+
+#define PORTRAIT "portrait"
+#define LANDSCAPE "Landscape"
+#define RESERVED "Reserved"
+struct mxc_spdc_resolution_map_para {
+ u16 resolution;
+ u16 res_x;
+ u16 res_y;
+ char res_name[12];
+};
+
+struct update_marker_data {
+ struct list_head full_list;
+ struct list_head upd_list;
+ u32 update_marker;
+ struct completion update_completion;
+ int lut_num;
+ bool collision_test;
+ bool waiting;
+};
+
+struct update_desc_list {
+ struct list_head list;
+ struct mxcfb_update_data upd_data;
+ u32 spdc_offs;
+ u32 spdc_stride;
+ struct list_head upd_marker_list;
+ u32 update_order;
+};
+
+/* This structure represents a list node containing both
+ * a memory region allocated as an output buffer for the PxP
+ * update processing task, and the update
+ * description (mode, region, etc.)
+ */
+struct update_data_list {
+ struct list_head list;
+ dma_addr_t phys_addr;/* Pointer to phys address of processed Y buf */
+ void *virt_addr;
+ struct update_desc_list *update_desc;
+ int lut_num;
+ u64 collision_mask; /* SPDC cannot support collision detect,
+ * align with EPDC driver struct */
+};
+
+typedef struct mxc_spdc_fb_param {
+ struct fb_info info;
+ struct fb_var_screeninfo spdc_fb_var;
+
+ char fw_str[24];
+ u32 pseudo_palette[16];
+ struct mxc_spdc_version spdc_ver;
+ struct imx_spdc_fb_mode *cur_mode;
+ struct imx_spdc_fb_platform_data *pdata;
+ struct partial_refresh_param fresh_param;
+ int blank;
+ bool updates_active;
+
+ u32 fb_offset;
+ u32 max_pix_size;
+ ssize_t map_size;
+ int native_width;
+ int native_height;
+ int default_bpp;
+
+ dma_addr_t phys_start;
+ void *virt_start;
+ struct device *dev;
+
+ int num_screens;
+ u32 order_cnt;
+ int max_num_updates;
+ u32 auto_mode;
+ u32 upd_scheme;
+ u32 operation_mode;
+ bool is_deep_fresh;
+ struct list_head full_marker_list;
+ struct list_head upd_pending_list;
+ struct list_head upd_buf_queue;
+ struct list_head upd_buf_free_list;
+ struct list_head upd_buf_preprocess_list;
+ u32 upd_preprocess_num;
+ int submit_upd_sta;
+ struct update_data_list *cur_update;
+ struct mutex queue_mutex;
+ struct imx_spdc_panel_init_set panel_set;
+ struct mxc_spdc_lut_para lut_para;
+ struct mxcfb_waveform_modes wv_modes;
+
+ dma_addr_t phy_next_buf;
+ dma_addr_t phy_pre_buf;
+ dma_addr_t phy_current_buf;
+ dma_addr_t phy_cnt_buf;
+ dma_addr_t phy_lut_buf;
+ void *virt_next_buf;
+ void *virt_current_buf;
+ void *virt_pre_buf;
+ void *virt_cnt_buf;
+ void *virt_lut_buf;
+ u32 next_buf_size;
+ u32 current_buf_size;
+ u32 pre_buf_size;
+ u32 cnt_buf_size;
+ u32 lut_buf_size;
+
+ dma_addr_t *phys_addr_updbuf;
+ void **virt_addr_updbuf;
+ u32 upd_buffer_num;
+ u32 max_num_buffers;
+
+ /* copy the processed data to next buffer relative region */
+ dma_addr_t phys_addr_copybuf;
+ void *virt_addr_copybuf;
+
+ int spdc_irq;
+ char __iomem *hwp;
+ struct clk *spdc_clk_axi;
+ struct clk *spdc_clk_pix;
+ struct regulator *display_regulator;
+ struct regulator *vcom_regulator;
+ struct regulator *v3p3_regulator;
+
+ spinlock_t lock;
+ int power_state;
+ bool powering_down;
+ int pwrdown_delay;
+ int wait_for_powerdown;
+ struct completion powerdown_compl;
+ struct mutex power_mutex;
+ bool fw_default_load;
+ bool hw_ready;
+ bool hw_initializing;
+ bool waiting_for_idle;
+ bool waiting_for_wb;
+ struct completion update_res_free;
+ struct completion lut_down;
+ struct completion init_finish;
+ struct completion update_finish;
+ struct completion updates_done;
+ struct delayed_work spdc_done_work;
+ struct workqueue_struct *spdc_submit_workqueue;
+ struct work_struct spdc_submit_work;
+ struct workqueue_struct *spdc_intr_workqueue;
+ struct work_struct spdc_intr_work;
+
+ /* FB elements related to PxP DMA */
+ struct completion pxp_tx_cmpl;
+ struct pxp_channel *pxp_chan;
+ struct pxp_config_data pxp_conf;
+ struct dma_async_tx_descriptor *txd;
+ dma_cookie_t cookie;
+ struct scatterlist sg[2];
+ struct mutex pxp_mutex; /* protects access to PxP */
+} mxc_spdc_t;
+
+#endif
diff --git a/drivers/video/mxc/mxcfb_seiko_wvga.c b/drivers/video/mxc/mxcfb_seiko_wvga.c
index 0ba4d9ed064f..c96238d80cb2 100644
--- a/drivers/video/mxc/mxcfb_seiko_wvga.c
+++ b/drivers/video/mxc/mxcfb_seiko_wvga.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
@@ -122,7 +122,7 @@ static struct notifier_block nb = {
static int __devinit lcd_probe(struct platform_device *pdev)
{
int i;
- struct mxc_lcd_platform_data *plat = pdev->dev.platform_data;
+ struct fsl_mxc_lcd_platform_data *plat = pdev->dev.platform_data;
if (plat) {
if (plat->reset)