/* * Copyright (C) 2010 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 * 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 STMP378X PxP driver * Copyright 2008-2009 Embedded Alley Solutions, Inc All Rights Reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "regs-pxp.h" #define PXP_DOWNSCALE_THRESHOLD 0x4000 static LIST_HEAD(head); struct pxp_dma { struct dma_device dma; }; struct pxps { struct platform_device *pdev; struct clk *clk; void __iomem *base; int irq; /* PXP IRQ to the CPU */ spinlock_t lock; struct mutex mutex; struct device *dev; struct pxp_dma pxp_dma; struct pxp_channel channel[NR_PXP_VIRT_CHANNEL]; struct work_struct work; struct workqueue_struct *workqueue; /* describes most recent processing configuration */ struct pxp_config_data pxp_conf_state; }; #define to_pxp_dma(d) container_of(d, struct pxp_dma, dma) #define to_tx_desc(tx) container_of(tx, struct pxp_tx_desc, txd) #define to_pxp_channel(d) container_of(d, struct pxp_channel, dma_chan) #define to_pxp(id) container_of(id, struct pxps, pxp_dma) #define PXP_DEF_BUFS 2 #define PXP_MIN_PIX 8 static uint32_t pxp_s0_formats[] = { PXP_PIX_FMT_RGB24, PXP_PIX_FMT_RGB565, PXP_PIX_FMT_RGB555, PXP_PIX_FMT_YUV420P, PXP_PIX_FMT_YUV422P, }; /* * PXP common functions */ static void dump_pxp_reg(struct pxps *pxp) { dev_err(pxp->dev, "PXP_CTRL 0x%x", __raw_readl(pxp->base + HW_PXP_CTRL)); dev_err(pxp->dev, "PXP_STAT 0x%x", __raw_readl(pxp->base + HW_PXP_STAT)); dev_err(pxp->dev, "PXP_OUTBUF 0x%x", __raw_readl(pxp->base + HW_PXP_OUTBUF)); dev_err(pxp->dev, "PXP_OUTBUF2 0x%x", __raw_readl(pxp->base + HW_PXP_OUTBUF2)); dev_err(pxp->dev, "PXP_OUTSIZE 0x%x", __raw_readl(pxp->base + HW_PXP_OUTSIZE)); dev_err(pxp->dev, "PXP_S0BUF 0x%x", __raw_readl(pxp->base + HW_PXP_S0BUF)); dev_err(pxp->dev, "PXP_S0UBUF 0x%x", __raw_readl(pxp->base + HW_PXP_S0UBUF)); dev_err(pxp->dev, "PXP_S0VBUF 0x%x", __raw_readl(pxp->base + HW_PXP_S0VBUF)); dev_err(pxp->dev, "PXP_S0PARAM 0x%x", __raw_readl(pxp->base + HW_PXP_S0PARAM)); dev_err(pxp->dev, "PXP_S0BACKGROUND 0x%x", __raw_readl(pxp->base + HW_PXP_S0BACKGROUND)); dev_err(pxp->dev, "PXP_S0CROP 0x%x", __raw_readl(pxp->base + HW_PXP_S0CROP)); dev_err(pxp->dev, "PXP_S0SCALE 0x%x", __raw_readl(pxp->base + HW_PXP_S0SCALE)); dev_err(pxp->dev, "PXP_OLn 0x%x", __raw_readl(pxp->base + HW_PXP_OLn(0))); dev_err(pxp->dev, "PXP_OLnSIZE 0x%x", __raw_readl(pxp->base + HW_PXP_OLnSIZE(0))); dev_err(pxp->dev, "PXP_OLnPARAM 0x%x", __raw_readl(pxp->base + HW_PXP_OLnPARAM(0))); dev_err(pxp->dev, "PXP_CSCCOEF0 0x%x", __raw_readl(pxp->base + HW_PXP_CSCCOEF0)); dev_err(pxp->dev, "PXP_CSC2CTRL 0x%x", __raw_readl(pxp->base + HW_PXP_CSC2CTRL)); dev_err(pxp->dev, "PXP_CSC2COEF0 0x%x", __raw_readl(pxp->base + HW_PXP_CSC2COEF0)); dev_err(pxp->dev, "PXP_CSC2COEF1 0x%x", __raw_readl(pxp->base + HW_PXP_CSC2COEF1)); dev_err(pxp->dev, "PXP_CSC2COEF2 0x%x", __raw_readl(pxp->base + HW_PXP_CSC2COEF2)); dev_err(pxp->dev, "PXP_CSC2COEF3 0x%x", __raw_readl(pxp->base + HW_PXP_CSC2COEF3)); dev_err(pxp->dev, "PXP_CSC2COEF4 0x%x", __raw_readl(pxp->base + HW_PXP_CSC2COEF4)); dev_err(pxp->dev, "PXP_CSC2COEF5 0x%x", __raw_readl(pxp->base + HW_PXP_CSC2COEF5)); dev_err(pxp->dev, "PXP_LUT_CTRL 0x%x", __raw_readl(pxp->base + HW_PXP_LUT_CTRL)); dev_err(pxp->dev, "PXP_LUT 0x%x", __raw_readl(pxp->base + HW_PXP_LUT)); dev_err(pxp->dev, "PXP_HIST_CTRL 0x%x", __raw_readl(pxp->base + HW_PXP_HIST_CTRL)); dev_err(pxp->dev, "PXP_HIST2_PARAM 0x%x", __raw_readl(pxp->base + HW_PXP_HIST2_PARAM)); dev_err(pxp->dev, "PXP_HIST4_PARAM 0x%x", __raw_readl(pxp->base + HW_PXP_HIST4_PARAM)); dev_err(pxp->dev, "PXP_HIST8_PARAM0 0x%x", __raw_readl(pxp->base + HW_PXP_HIST8_PARAM0)); dev_err(pxp->dev, "PXP_HIST8_PARAM1 0x%x", __raw_readl(pxp->base + HW_PXP_HIST8_PARAM1)); dev_err(pxp->dev, "PXP_HIST16_PARAM0 0x%x", __raw_readl(pxp->base + HW_PXP_HIST16_PARAM0)); dev_err(pxp->dev, "PXP_HIST16_PARAM1 0x%x", __raw_readl(pxp->base + HW_PXP_HIST16_PARAM1)); dev_err(pxp->dev, "PXP_HIST16_PARAM2 0x%x", __raw_readl(pxp->base + HW_PXP_HIST16_PARAM2)); dev_err(pxp->dev, "PXP_HIST16_PARAM3 0x%x", __raw_readl(pxp->base + HW_PXP_HIST16_PARAM3)); } static bool is_yuv(pix_fmt) { if ((pix_fmt == PXP_PIX_FMT_YUYV) | (pix_fmt == PXP_PIX_FMT_UYVY) | (pix_fmt == PXP_PIX_FMT_Y41P) | (pix_fmt == PXP_PIX_FMT_YUV444) | (pix_fmt == PXP_PIX_FMT_NV12) | (pix_fmt == PXP_PIX_FMT_GREY) | (pix_fmt == PXP_PIX_FMT_YVU410P) | (pix_fmt == PXP_PIX_FMT_YUV410P) | (pix_fmt == PXP_PIX_FMT_YVU420P) | (pix_fmt == PXP_PIX_FMT_YUV420P) | (pix_fmt == PXP_PIX_FMT_YUV420P2) | (pix_fmt == PXP_PIX_FMT_YVU422P) | (pix_fmt == PXP_PIX_FMT_YUV422P)) { return true; } else { return false; } } static void pxp_set_ctrl(struct pxps *pxp) { struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; struct pxp_proc_data *proc_data = &pxp_conf->proc_data; u32 ctrl; u32 fmt_ctrl; /* Configure S0 input format */ switch (pxp_conf->s0_param.pixel_fmt) { case PXP_PIX_FMT_RGB24: fmt_ctrl = BV_PXP_CTRL_S0_FORMAT__RGB888; break; case PXP_PIX_FMT_RGB565: fmt_ctrl = BV_PXP_CTRL_S0_FORMAT__RGB565; break; case PXP_PIX_FMT_RGB555: fmt_ctrl = BV_PXP_CTRL_S0_FORMAT__RGB555; break; case PXP_PIX_FMT_YUV420P: case PXP_PIX_FMT_GREY: fmt_ctrl = BV_PXP_CTRL_S0_FORMAT__YUV420; break; case PXP_PIX_FMT_YUV422P: fmt_ctrl = BV_PXP_CTRL_S0_FORMAT__YUV422; break; default: fmt_ctrl = 0; } ctrl = BF_PXP_CTRL_S0_FORMAT(fmt_ctrl); /* Configure output format based on out_channel format */ switch (pxp_conf->out_param.pixel_fmt) { case PXP_PIX_FMT_RGB24: fmt_ctrl = BV_PXP_CTRL_OUTBUF_FORMAT__RGB888; break; case PXP_PIX_FMT_RGB565: fmt_ctrl = BV_PXP_CTRL_OUTBUF_FORMAT__RGB565; break; case PXP_PIX_FMT_RGB555: fmt_ctrl = BV_PXP_CTRL_OUTBUF_FORMAT__RGB555; break; case PXP_PIX_FMT_YUV420P: fmt_ctrl = BV_PXP_CTRL_OUTBUF_FORMAT__YUV2P420; break; case PXP_PIX_FMT_YUV422P: fmt_ctrl = BV_PXP_CTRL_OUTBUF_FORMAT__YUV2P422; break; case PXP_PIX_FMT_GREY: fmt_ctrl = BV_PXP_CTRL_OUTBUF_FORMAT__MONOC8; break; default: fmt_ctrl = 0; } ctrl |= BF_PXP_CTRL_OUTBUF_FORMAT(fmt_ctrl); ctrl |= BM_PXP_CTRL_CROP; if (proc_data->scaling) ctrl |= BM_PXP_CTRL_SCALE; if (proc_data->vflip) ctrl |= BM_PXP_CTRL_VFLIP; if (proc_data->hflip) ctrl |= BM_PXP_CTRL_HFLIP; if (proc_data->rotate) ctrl |= BF_PXP_CTRL_ROTATE(proc_data->rotate / 90); __raw_writel(ctrl, pxp->base + HW_PXP_CTRL); } static int pxp_start(struct pxps *pxp) { __raw_writel(BM_PXP_CTRL_IRQ_ENABLE, pxp->base + HW_PXP_CTRL_SET); __raw_writel(BM_PXP_CTRL_ENABLE, pxp->base + HW_PXP_CTRL_SET); return 0; } static void pxp_set_outbuf(struct pxps *pxp) { struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; struct pxp_layer_param *out_params = &pxp_conf->out_param; __raw_writel(out_params->paddr, pxp->base + HW_PXP_OUTBUF); __raw_writel(BF_PXP_OUTSIZE_WIDTH(out_params->width) | BF_PXP_OUTSIZE_HEIGHT(out_params->height), pxp->base + HW_PXP_OUTSIZE); } static void pxp_set_s0colorkey(struct pxps *pxp) { struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; struct pxp_layer_param *s0_params = &pxp_conf->s0_param; /* Low and high are set equal. V4L does not allow a chromakey range */ if (s0_params->color_key == -1) { /* disable color key */ __raw_writel(0xFFFFFF, pxp->base + HW_PXP_S0COLORKEYLOW); __raw_writel(0, pxp->base + HW_PXP_S0COLORKEYHIGH); } else { __raw_writel(s0_params->color_key, pxp->base + HW_PXP_S0COLORKEYLOW); __raw_writel(s0_params->color_key, pxp->base + HW_PXP_S0COLORKEYHIGH); } } static void pxp_set_olcolorkey(int layer_no, struct pxps *pxp) { struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; struct pxp_layer_param *ol_params = &pxp_conf->ol_param[layer_no]; /* Low and high are set equal. V4L does not allow a chromakey range */ if (ol_params->color_key_enable != 0 && ol_params->color_key != -1) { __raw_writel(ol_params->color_key, pxp->base + HW_PXP_OLCOLORKEYLOW); __raw_writel(ol_params->color_key, pxp->base + HW_PXP_OLCOLORKEYHIGH); } else { /* disable color key */ __raw_writel(0xFFFFFF, pxp->base + HW_PXP_OLCOLORKEYLOW); __raw_writel(0, pxp->base + HW_PXP_OLCOLORKEYHIGH); } } static void pxp_set_oln(int layer_no, struct pxps *pxp) { struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; struct pxp_layer_param *olparams_data = &pxp_conf->ol_param[layer_no]; dma_addr_t phys_addr = olparams_data->paddr; __raw_writel(phys_addr, pxp->base + HW_PXP_OLn(layer_no)); /* Fixme */ __raw_writel(BF_PXP_OLnSIZE_WIDTH(olparams_data->width >> 3) | BF_PXP_OLnSIZE_HEIGHT(olparams_data->height >> 3), pxp->base + HW_PXP_OLnSIZE(layer_no)); } static void pxp_set_olparam(int layer_no, struct pxps *pxp) { struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; struct pxp_layer_param *olparams_data = &pxp_conf->ol_param[layer_no]; u32 olparam; olparam = BF_PXP_OLnPARAM_ALPHA(olparams_data->global_alpha); if (olparams_data->pixel_fmt == PXP_PIX_FMT_RGB24) olparam |= BF_PXP_OLnPARAM_FORMAT(BV_PXP_OLnPARAM_FORMAT__RGB888); else olparam |= BF_PXP_OLnPARAM_FORMAT(BV_PXP_OLnPARAM_FORMAT__RGB565); if (olparams_data->global_alpha) olparam |= BF_PXP_OLnPARAM_ALPHA_CNTL (BV_PXP_OLnPARAM_ALPHA_CNTL__Override); if (olparams_data->color_key_enable) olparam |= BM_PXP_OLnPARAM_ENABLE_COLORKEY; if (olparams_data->combine_enable) olparam |= BM_PXP_OLnPARAM_ENABLE; __raw_writel(olparam, pxp->base + HW_PXP_OLnPARAM(layer_no)); } static void pxp_set_s0param(struct pxps *pxp) { struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; struct pxp_layer_param *s0params_data = &pxp_conf->s0_param; struct pxp_proc_data *proc_data = &pxp_conf->proc_data; u32 s0param; s0param = BF_PXP_S0PARAM_XBASE(proc_data->drect.left >> 3); s0param |= BF_PXP_S0PARAM_YBASE(proc_data->drect.top >> 3); s0param |= BF_PXP_S0PARAM_WIDTH(s0params_data->width >> 3); s0param |= BF_PXP_S0PARAM_HEIGHT(s0params_data->height >> 3); __raw_writel(s0param, pxp->base + HW_PXP_S0PARAM); } static void pxp_set_s0crop(struct pxps *pxp) { u32 s0crop; struct pxp_proc_data *proc_data = &pxp->pxp_conf_state.proc_data; s0crop = BF_PXP_S0CROP_XBASE(proc_data->srect.left >> 3); s0crop |= BF_PXP_S0CROP_YBASE(proc_data->srect.top >> 3); s0crop |= BF_PXP_S0CROP_WIDTH(proc_data->drect.width >> 3); s0crop |= BF_PXP_S0CROP_HEIGHT(proc_data->drect.height >> 3); __raw_writel(s0crop, pxp->base + HW_PXP_S0CROP); } static int pxp_set_scaling(struct pxps *pxp) { int ret = 0; u32 xscale, yscale, s0scale; struct pxp_proc_data *proc_data = &pxp->pxp_conf_state.proc_data; struct pxp_layer_param *s0params_data = &pxp->pxp_conf_state.s0_param; if ((s0params_data->pixel_fmt != PXP_PIX_FMT_YUV420P) && (s0params_data->pixel_fmt != PXP_PIX_FMT_YUV422P)) { proc_data->scaling = 0; ret = -EINVAL; goto out; } if ((proc_data->srect.width == proc_data->drect.width) && (proc_data->srect.height == proc_data->drect.height)) { proc_data->scaling = 0; __raw_writel(0x10001000, pxp->base + HW_PXP_S0SCALE); goto out; } proc_data->scaling = 1; xscale = proc_data->srect.width * 0x1000 / proc_data->drect.width; yscale = proc_data->srect.height * 0x1000 / proc_data->drect.height; if (xscale > PXP_DOWNSCALE_THRESHOLD) xscale = PXP_DOWNSCALE_THRESHOLD; if (yscale > PXP_DOWNSCALE_THRESHOLD) yscale = PXP_DOWNSCALE_THRESHOLD; s0scale = BF_PXP_S0SCALE_YSCALE(yscale) | BF_PXP_S0SCALE_XSCALE(xscale); __raw_writel(s0scale, pxp->base + HW_PXP_S0SCALE); out: pxp_set_ctrl(pxp); return ret; } static void pxp_set_bg(struct pxps *pxp) { __raw_writel(pxp->pxp_conf_state.proc_data.bgcolor, pxp->base + HW_PXP_S0BACKGROUND); } static void pxp_set_lut(struct pxps *pxp) { struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; u32 reg_val; int i; if (pxp_conf->proc_data.lut_transform == PXP_LUT_NONE) { __raw_writel(BM_PXP_LUT_CTRL_BYPASS, pxp->base + HW_PXP_LUT_CTRL); } else if (pxp_conf->proc_data.lut_transform == PXP_LUT_INVERT) { /* Fill out LUT table with 8-bit inverted values */ /* Initialize LUT address to 0 and clear bypass bit */ __raw_writel(0, pxp->base + HW_PXP_LUT_CTRL); /* LUT address pointer auto-increments after each data write */ for (i = 0; i < 256; i++) { reg_val = __raw_readl(pxp->base + HW_PXP_LUT_CTRL) & BM_PXP_LUT_CTRL_ADDR; reg_val = ~reg_val & BM_PXP_LUT_DATA; __raw_writel(reg_val, pxp->base + HW_PXP_LUT); } } } static void pxp_set_csc(struct pxps *pxp) { struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; struct pxp_layer_param *s0_params = &pxp_conf->s0_param; struct pxp_layer_param *ol_params = &pxp_conf->ol_param[0]; struct pxp_layer_param *out_params = &pxp_conf->out_param; bool input_is_YUV = is_yuv(s0_params->pixel_fmt); bool output_is_YUV = is_yuv(out_params->pixel_fmt); if (input_is_YUV && output_is_YUV) { /* * Input = YUV, Output = YUV * No CSC unless we need to do combining */ if (ol_params->combine_enable) { /* Must convert to RGB for combining with RGB overlay */ /* CSC1 - YUV->RGB */ __raw_writel(0x04030000, pxp->base + HW_PXP_CSCCOEF0); __raw_writel(0x01230208, pxp->base + HW_PXP_CSCCOEF1); __raw_writel(0x076b079c, pxp->base + HW_PXP_CSCCOEF2); /* CSC2 - RGB->YUV */ __raw_writel(0x4, pxp->base + HW_PXP_CSC2CTRL); __raw_writel(0x0096004D, pxp->base + HW_PXP_CSC2COEF0); __raw_writel(0x05DA001D, pxp->base + HW_PXP_CSC2COEF1); __raw_writel(0x007005B6, pxp->base + HW_PXP_CSC2COEF2); __raw_writel(0x057C009E, pxp->base + HW_PXP_CSC2COEF3); __raw_writel(0x000005E6, pxp->base + HW_PXP_CSC2COEF4); __raw_writel(0x00000000, pxp->base + HW_PXP_CSC2COEF5); } else { /* Input & Output both YUV, so bypass both CSCs */ /* CSC1 - Bypass */ __raw_writel(0x40000000, pxp->base + HW_PXP_CSCCOEF0); /* CSC2 - Bypass */ __raw_writel(0x1, pxp->base + HW_PXP_CSC2CTRL); } } else if (input_is_YUV && !output_is_YUV) { /* * Input = YUV, Output = RGB * Use CSC1 to convert to RGB */ /* CSC1 - YUV->RGB */ __raw_writel(0x04030000, pxp->base + HW_PXP_CSCCOEF0); __raw_writel(0x01230208, pxp->base + HW_PXP_CSCCOEF1); __raw_writel(0x076b079c, pxp->base + HW_PXP_CSCCOEF2); /* CSC2 - Bypass */ __raw_writel(0x1, pxp->base + HW_PXP_CSC2CTRL); } else if (!input_is_YUV && output_is_YUV) { /* * Input = RGB, Output = YUV * Use CSC2 to convert to YUV */ /* CSC1 - Bypass */ __raw_writel(0x40000000, pxp->base + HW_PXP_CSCCOEF0); /* CSC2 - RGB->YUV */ __raw_writel(0x4, pxp->base + HW_PXP_CSC2CTRL); __raw_writel(0x0096004D, pxp->base + HW_PXP_CSC2COEF0); __raw_writel(0x05DA001D, pxp->base + HW_PXP_CSC2COEF1); __raw_writel(0x007005B6, pxp->base + HW_PXP_CSC2COEF2); __raw_writel(0x057C009E, pxp->base + HW_PXP_CSC2COEF3); __raw_writel(0x000005E6, pxp->base + HW_PXP_CSC2COEF4); __raw_writel(0x00000000, pxp->base + HW_PXP_CSC2COEF5); } else { /* * Input = RGB, Output = RGB * Input & Output both RGB, so bypass both CSCs */ /* CSC1 - Bypass */ __raw_writel(0x40000000, pxp->base + HW_PXP_CSCCOEF0); /* CSC2 - Bypass */ __raw_writel(0x1, pxp->base + HW_PXP_CSC2CTRL); } /* YCrCb colorspace */ /* Not sure when we use this...no YCrCb formats are defined for PxP */ /* __raw_writel(0x84ab01f0, HW_PXP_CSCCOEFF0_ADDR); __raw_writel(0x01230204, HW_PXP_CSCCOEFF1_ADDR); __raw_writel(0x0730079c, HW_PXP_CSCCOEFF2_ADDR); */ } static void pxp_set_s0buf(struct pxps *pxp) { struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; struct pxp_layer_param *s0_params = &pxp_conf->s0_param; dma_addr_t Y, U, V; Y = s0_params->paddr; __raw_writel(Y, pxp->base + HW_PXP_S0BUF); if ((s0_params->pixel_fmt == PXP_PIX_FMT_YUV420P) || (s0_params->pixel_fmt == PXP_PIX_FMT_YVU420P) || (s0_params->pixel_fmt == PXP_PIX_FMT_GREY)) { /* Set to 1 if YUV format is 4:2:2 rather than 4:2:0 */ int s = 2; U = Y + (s0_params->width * s0_params->height); V = U + ((s0_params->width * s0_params->height) >> s); __raw_writel(U, pxp->base + HW_PXP_S0UBUF); __raw_writel(V, pxp->base + HW_PXP_S0VBUF); } } /** * pxp_config() - configure PxP for a processing task * @pxps: PXP context. * @pxp_chan: PXP channel. * @return: 0 on success or negative error code on failure. */ static int pxp_config(struct pxps *pxp, struct pxp_channel *pxp_chan) { struct pxp_config_data *pxp_conf_data = &pxp->pxp_conf_state; int ol_nr; int i; /* Configure PxP regs */ pxp_set_ctrl(pxp); pxp_set_s0param(pxp); pxp_set_s0crop(pxp); pxp_set_scaling(pxp); ol_nr = pxp_conf_data->layer_nr - 2; while (ol_nr > 0) { i = pxp_conf_data->layer_nr - 2 - ol_nr; pxp_set_oln(i, pxp); pxp_set_olparam(i, pxp); /* only the color key in higher overlay will take effect. */ pxp_set_olcolorkey(i, pxp); ol_nr--; } pxp_set_s0colorkey(pxp); pxp_set_csc(pxp); pxp_set_bg(pxp); pxp_set_lut(pxp); pxp_set_s0buf(pxp); pxp_set_outbuf(pxp); return 0; } static struct pxp_tx_desc *pxpdma_first_active(struct pxp_channel *pxp_chan) { return list_entry(pxp_chan->active_list.next, struct pxp_tx_desc, list); } static struct pxp_tx_desc *pxpdma_first_queued(struct pxp_channel *pxp_chan) { return list_entry(pxp_chan->queue.next, struct pxp_tx_desc, list); } /* called with pxp_chan->lock held */ static void __pxpdma_dostart(struct pxp_channel *pxp_chan) { struct pxp_dma *pxp_dma = to_pxp_dma(pxp_chan->dma_chan.device); struct pxps *pxp = to_pxp(pxp_dma); struct pxp_tx_desc *desc; struct pxp_tx_desc *child; int i = 0; /* so far we presume only one transaction on active_list */ /* S0 */ desc = pxpdma_first_active(pxp_chan); memcpy(&pxp->pxp_conf_state.s0_param, &desc->layer_param.s0_param, sizeof(struct pxp_layer_param)); memcpy(&pxp->pxp_conf_state.proc_data, &desc->proc_data, sizeof(struct pxp_proc_data)); /* Save PxP configuration */ list_for_each_entry(child, &desc->txd.tx_list, list) { if (i == 0) { /* Output */ memcpy(&pxp->pxp_conf_state.out_param, &child->layer_param.out_param, sizeof(struct pxp_layer_param)); } else { /* Overlay */ memcpy(&pxp->pxp_conf_state.ol_param[i - 1], &child->layer_param.ol_param, sizeof(struct pxp_layer_param)); } i++; } pr_debug("%s:%d S0 w/h %d/%d paddr %08x\n", __func__, __LINE__, pxp->pxp_conf_state.s0_param.width, pxp->pxp_conf_state.s0_param.height, pxp->pxp_conf_state.s0_param.paddr); pr_debug("%s:%d OUT w/h %d/%d paddr %08x\n", __func__, __LINE__, pxp->pxp_conf_state.out_param.width, pxp->pxp_conf_state.out_param.height, pxp->pxp_conf_state.out_param.paddr); } static void pxpdma_dostart_work(struct work_struct *w) { struct pxps *pxp = container_of(w, struct pxps, work); struct pxp_channel *pxp_chan = NULL; unsigned long flags, flags1; int val; val = __raw_readl(pxp->base + HW_PXP_CTRL); if (val & BM_PXP_CTRL_ENABLE) { pr_warning("pxp is active, quit.\n"); return; } spin_lock_irqsave(&pxp->lock, flags); if (list_empty(&head)) { spin_unlock_irqrestore(&pxp->lock, flags); return; } pxp_chan = list_entry(head.next, struct pxp_channel, list); spin_lock_irqsave(&pxp_chan->lock, flags1); if (!list_empty(&pxp_chan->active_list)) { struct pxp_tx_desc *desc; /* REVISIT */ desc = pxpdma_first_active(pxp_chan); __pxpdma_dostart(pxp_chan); } spin_unlock_irqrestore(&pxp_chan->lock, flags1); /* Configure PxP */ pxp_config(pxp, pxp_chan); pxp_start(pxp); spin_unlock_irqrestore(&pxp->lock, flags); } static void pxpdma_dequeue(struct pxp_channel *pxp_chan, struct list_head *list) { struct pxp_tx_desc *desc = NULL; do { desc = pxpdma_first_queued(pxp_chan); list_move_tail(&desc->list, list); } while (!list_empty(&pxp_chan->queue)); } static dma_cookie_t pxp_tx_submit(struct dma_async_tx_descriptor *tx) { struct pxp_tx_desc *desc = to_tx_desc(tx); struct pxp_channel *pxp_chan = to_pxp_channel(tx->chan); dma_cookie_t cookie; unsigned long flags; dev_dbg(&pxp_chan->dma_chan.dev->device, "received TX\n"); mutex_lock(&pxp_chan->chan_mutex); cookie = pxp_chan->dma_chan.cookie; if (++cookie < 0) cookie = 1; /* from dmaengine.h: "last cookie value returned to client" */ pxp_chan->dma_chan.cookie = cookie; tx->cookie = cookie; /* pxp_chan->lock can be taken under ichan->lock, but not v.v. */ spin_lock_irqsave(&pxp_chan->lock, flags); /* Here we add the tx descriptor to our PxP task queue. */ list_add_tail(&desc->list, &pxp_chan->queue); spin_unlock_irqrestore(&pxp_chan->lock, flags); dev_dbg(&pxp_chan->dma_chan.dev->device, "done TX\n"); mutex_unlock(&pxp_chan->chan_mutex); return cookie; } /* Called with pxp_chan->chan_mutex held */ static int pxp_desc_alloc(struct pxp_channel *pxp_chan, int n) { struct pxp_tx_desc *desc = vmalloc(n * sizeof(struct pxp_tx_desc)); if (!desc) return -ENOMEM; pxp_chan->n_tx_desc = n; pxp_chan->desc = desc; INIT_LIST_HEAD(&pxp_chan->active_list); INIT_LIST_HEAD(&pxp_chan->queue); INIT_LIST_HEAD(&pxp_chan->free_list); while (n--) { struct dma_async_tx_descriptor *txd = &desc->txd; memset(txd, 0, sizeof(*txd)); dma_async_tx_descriptor_init(txd, &pxp_chan->dma_chan); txd->tx_submit = pxp_tx_submit; list_add(&desc->list, &pxp_chan->free_list); desc++; } return 0; } /** * pxp_init_channel() - initialize a PXP channel. * @pxp_dma: PXP DMA context. * @pchan: pointer to the channel object. * @return 0 on success or negative error code on failure. */ static int pxp_init_channel(struct pxp_dma *pxp_dma, struct pxp_channel *pxp_chan) { unsigned long flags; struct pxps *pxp = to_pxp(pxp_dma); int ret = 0, n_desc = 0; /* * We are using _virtual_ channel here. * Each channel contains all parameters of corresponding layers * for one transaction; each layer is represented as one descriptor * (i.e., pxp_tx_desc) here. */ spin_lock_irqsave(&pxp->lock, flags); /* max desc nr: S0+OL+OUT = 1+8+1 */ n_desc = 10; spin_unlock_irqrestore(&pxp->lock, flags); if (n_desc && !pxp_chan->desc) ret = pxp_desc_alloc(pxp_chan, n_desc); return ret; } /** * pxp_uninit_channel() - uninitialize a PXP channel. * @pxp_dma: PXP DMA context. * @pchan: pointer to the channel object. * @return 0 on success or negative error code on failure. */ static int pxp_uninit_channel(struct pxp_dma *pxp_dma, struct pxp_channel *pxp_chan) { int ret = 0; if (pxp_chan->desc) vfree(pxp_chan->desc); pxp_chan->desc = NULL; return ret; } static irqreturn_t pxp_irq(int irq, void *dev_id) { struct pxp_channel *pxp_chan = dev_id; struct pxp_dma *pxp_dma = to_pxp_dma(pxp_chan->dma_chan.device); struct pxps *pxp = to_pxp(pxp_dma); struct pxp_tx_desc *desc; dma_async_tx_callback callback; void *callback_param; unsigned long flags; u32 hist_status; hist_status = __raw_readl(pxp->base + HW_PXP_HIST_CTRL) & BM_PXP_HIST_CTRL_STATUS; __raw_writel(BM_PXP_STAT_IRQ, pxp->base + HW_PXP_STAT_CLR); spin_lock_irqsave(&pxp->lock, flags); if (list_empty(&pxp_chan->active_list)) { pr_debug("PXP_IRQ pxp_chan->active_list empty. chan_id %d\n", pxp_chan->dma_chan.chan_id); spin_unlock_irqrestore(&pxp->lock, flags); return IRQ_NONE; } /* Get descriptor and call callback */ desc = pxpdma_first_active(pxp_chan); pxp_chan->completed = desc->txd.cookie; callback = desc->txd.callback; callback_param = desc->txd.callback_param; /* Send histogram status back to caller */ desc->hist_status = hist_status; if ((desc->txd.flags & DMA_PREP_INTERRUPT) && callback) callback(callback_param); pxp_chan->status = PXP_CHANNEL_INITIALIZED; list_splice_init(&desc->txd.tx_list, &pxp_chan->free_list); list_move(&desc->list, &pxp_chan->free_list); list_del(&pxp_chan->list); spin_unlock_irqrestore(&pxp->lock, flags); queue_work(pxp->workqueue, &pxp->work); return IRQ_HANDLED; } static struct pxp_tx_desc *pxp_desc_get(struct pxp_channel *pxp_chan) { struct pxp_tx_desc *desc, *_desc; struct pxp_tx_desc *ret = NULL; unsigned long flags; spin_lock_irqsave(&pxp_chan->lock, flags); list_for_each_entry_safe(desc, _desc, &pxp_chan->free_list, list) { list_del_init(&desc->list); ret = desc; break; } spin_unlock_irqrestore(&pxp_chan->lock, flags); return ret; } static void pxpdma_desc_put(struct pxp_channel *pxp_chan, struct pxp_tx_desc *desc) { if (desc) { struct device *dev = &pxp_chan->dma_chan.dev->device; struct pxp_tx_desc *child; unsigned long flags; spin_lock_irqsave(&pxp_chan->lock, flags); list_for_each_entry(child, &desc->txd.tx_list, list) dev_info(dev, "moving child desc %p to freelist\n", child); list_splice_init(&desc->txd.tx_list, &pxp_chan->free_list); dev_info(dev, "moving desc %p to freelist\n", desc); list_add(&desc->list, &pxp_chan->free_list); spin_unlock_irqrestore(&pxp_chan->lock, flags); } } /* Allocate and initialise a transfer descriptor. */ static struct dma_async_tx_descriptor *pxp_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_data_direction direction, unsigned long tx_flags) { struct pxp_channel *pxp_chan = to_pxp_channel(chan); struct pxp_dma *pxp_dma = to_pxp_dma(chan->device); struct pxps *pxp = to_pxp(pxp_dma); struct pxp_tx_desc *desc = NULL; struct pxp_tx_desc *first = NULL, *prev = NULL; struct scatterlist *sg; unsigned long flags; dma_addr_t phys_addr; int i; if (direction != DMA_FROM_DEVICE && direction != DMA_TO_DEVICE) { dev_err(chan->device->dev, "Invalid DMA direction %d!\n", direction); return NULL; } if (unlikely(sg_len < 2)) return NULL; spin_lock_irqsave(&pxp_chan->lock, flags); for_each_sg(sgl, sg, sg_len, i) { desc = pxp_desc_get(pxp_chan); if (!desc) { pxpdma_desc_put(pxp_chan, first); dev_err(chan->device->dev, "Can't get DMA desc.\n"); spin_unlock_irqrestore(&pxp_chan->lock, flags); return NULL; } phys_addr = sg_dma_address(sg); if (!first) { first = desc; desc->layer_param.s0_param.paddr = phys_addr; } else { list_add_tail(&desc->list, &first->txd.tx_list); prev->next = desc; desc->next = NULL; if (i == 1) desc->layer_param.out_param.paddr = phys_addr; else desc->layer_param.ol_param.paddr = phys_addr; } prev = desc; } spin_unlock_irqrestore(&pxp_chan->lock, flags); pxp->pxp_conf_state.layer_nr = sg_len; first->txd.flags = tx_flags; first->len = sg_len; pr_debug("%s:%d first %p, first->len %d, flags %08x\n", __func__, __LINE__, first, first->len, first->txd.flags); return &first->txd; } static void pxp_issue_pending(struct dma_chan *chan) { struct pxp_channel *pxp_chan = to_pxp_channel(chan); struct pxp_dma *pxp_dma = to_pxp_dma(chan->device); struct pxps *pxp = to_pxp(pxp_dma); unsigned long flags0, flags; spin_lock_irqsave(&pxp->lock, flags0); spin_lock_irqsave(&pxp_chan->lock, flags); if (!list_empty(&pxp_chan->active_list)) queue_work(pxp->workqueue, &pxp->work); if (!list_empty(&pxp_chan->queue)) { pxpdma_dequeue(pxp_chan, &pxp_chan->active_list); pxp_chan->status = PXP_CHANNEL_READY; list_add_tail(&pxp_chan->list, &head); queue_work(pxp->workqueue, &pxp->work); } spin_unlock_irqrestore(&pxp_chan->lock, flags); spin_unlock_irqrestore(&pxp->lock, flags0); } static void __pxp_terminate_all(struct dma_chan *chan) { struct pxp_channel *pxp_chan = to_pxp_channel(chan); struct pxp_dma *pxp_dma = to_pxp_dma(chan->device); unsigned long flags; cancel_work_sync(&to_pxp(pxp_dma)->work); /* pchan->queue is modified in ISR, have to spinlock */ spin_lock_irqsave(&pxp_chan->lock, flags); list_splice_init(&pxp_chan->queue, &pxp_chan->free_list); list_splice_init(&pxp_chan->active_list, &pxp_chan->free_list); spin_unlock_irqrestore(&pxp_chan->lock, flags); pxp_chan->status = PXP_CHANNEL_INITIALIZED; } static void pxp_terminate_all(struct dma_chan *chan) { struct pxp_channel *pxp_chan = to_pxp_channel(chan); mutex_lock(&pxp_chan->chan_mutex); __pxp_terminate_all(chan); mutex_unlock(&pxp_chan->chan_mutex); } static int pxp_alloc_chan_resources(struct dma_chan *chan) { struct pxp_channel *pxp_chan = to_pxp_channel(chan); struct pxp_dma *pxp_dma = to_pxp_dma(chan->device); int ret; /* dmaengine.c now guarantees to only offer free channels */ BUG_ON(chan->client_count > 1); WARN_ON(pxp_chan->status != PXP_CHANNEL_FREE); chan->cookie = 1; pxp_chan->completed = -ENXIO; pr_debug("%s dma_chan.chan_id %d\n", __func__, chan->chan_id); ret = pxp_init_channel(pxp_dma, pxp_chan); if (ret < 0) goto err_chan; ret = request_irq(pxp_chan->eof_irq, pxp_irq, IRQF_SHARED, "pxp-irq", pxp_chan); if (ret < 0) goto err_irq; pxp_chan->status = PXP_CHANNEL_INITIALIZED; dev_dbg(&chan->dev->device, "Found channel 0x%x, irq %d\n", chan->chan_id, pxp_chan->eof_irq); return ret; err_irq: pxp_uninit_channel(pxp_dma, pxp_chan); err_chan: return ret; } static void pxp_free_chan_resources(struct dma_chan *chan) { struct pxp_channel *pxp_chan = to_pxp_channel(chan); struct pxp_dma *pxp_dma = to_pxp_dma(chan->device); mutex_lock(&pxp_chan->chan_mutex); __pxp_terminate_all(chan); pxp_chan->status = PXP_CHANNEL_FREE; pxp_uninit_channel(pxp_dma, pxp_chan); free_irq(pxp_chan->eof_irq, pxp_chan); mutex_unlock(&pxp_chan->chan_mutex); } static enum dma_status pxp_is_tx_complete(struct dma_chan *chan, dma_cookie_t cookie, dma_cookie_t *done, dma_cookie_t *used) { struct pxp_channel *pxp_chan = to_pxp_channel(chan); if (done) *done = pxp_chan->completed; if (used) *used = chan->cookie; if (cookie != chan->cookie) return DMA_ERROR; return DMA_SUCCESS; } static int pxp_hw_init(struct pxps *pxp) { struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; struct pxp_proc_data *proc_data = &pxp_conf->proc_data; u32 reg_val; int i; /* Pull PxP out of reset */ __raw_writel(0, pxp->base + HW_PXP_CTRL); /* Config defaults */ /* 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 = 0; proc_data->drect.height = proc_data->srect.height = 0; proc_data->scaling = 0; proc_data->hflip = 0; proc_data->vflip = 0; proc_data->rotate = 0; proc_data->bgcolor = 0; /* Initialize S0 channel parameters */ pxp_conf->s0_param.pixel_fmt = pxp_s0_formats[0]; pxp_conf->s0_param.width = 0; pxp_conf->s0_param.height = 0; pxp_conf->s0_param.color_key = -1; pxp_conf->s0_param.color_key_enable = false; /* Initialize OL channel parameters */ 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 */ pxp_conf->out_param.width = 0; pxp_conf->out_param.height = 0; pxp_conf->out_param.pixel_fmt = PXP_PIX_FMT_RGB565; proc_data->overlay_state = 0; /* Write default h/w config */ pxp_set_ctrl(pxp); pxp_set_s0param(pxp); pxp_set_s0crop(pxp); for (i = 0; i < 8; i++) { pxp_set_oln(i, pxp); pxp_set_olparam(i, pxp); pxp_set_olcolorkey(i, pxp); } pxp_set_s0colorkey(pxp); pxp_set_csc(pxp); pxp_set_bg(pxp); pxp_set_lut(pxp); /* One-time histogram configuration */ reg_val = BF_PXP_HIST_CTRL_PANEL_MODE(BV_PXP_HIST_CTRL_PANEL_MODE__GRAY16); __raw_writel(reg_val, pxp->base + HW_PXP_HIST_CTRL); reg_val = BF_PXP_HIST2_PARAM_VALUE0(0x00) | BF_PXP_HIST2_PARAM_VALUE1(0x00F); __raw_writel(reg_val, pxp->base + HW_PXP_HIST2_PARAM); reg_val = BF_PXP_HIST4_PARAM_VALUE0(0x00) | BF_PXP_HIST4_PARAM_VALUE1(0x05) | BF_PXP_HIST4_PARAM_VALUE2(0x0A) | BF_PXP_HIST4_PARAM_VALUE3(0x0F); __raw_writel(reg_val, pxp->base + HW_PXP_HIST4_PARAM); reg_val = BF_PXP_HIST8_PARAM0_VALUE0(0x00) | BF_PXP_HIST8_PARAM0_VALUE1(0x02) | BF_PXP_HIST8_PARAM0_VALUE2(0x04) | BF_PXP_HIST8_PARAM0_VALUE3(0x06); __raw_writel(reg_val, pxp->base + HW_PXP_HIST8_PARAM0); reg_val = BF_PXP_HIST8_PARAM1_VALUE4(0x09) | BF_PXP_HIST8_PARAM1_VALUE5(0x0B) | BF_PXP_HIST8_PARAM1_VALUE6(0x0D) | BF_PXP_HIST8_PARAM1_VALUE7(0x0F); __raw_writel(reg_val, pxp->base + HW_PXP_HIST8_PARAM1); reg_val = BF_PXP_HIST16_PARAM0_VALUE0(0x00) | BF_PXP_HIST16_PARAM0_VALUE1(0x01) | BF_PXP_HIST16_PARAM0_VALUE2(0x02) | BF_PXP_HIST16_PARAM0_VALUE3(0x03); __raw_writel(reg_val, pxp->base + HW_PXP_HIST16_PARAM0); reg_val = BF_PXP_HIST16_PARAM1_VALUE4(0x04) | BF_PXP_HIST16_PARAM1_VALUE5(0x05) | BF_PXP_HIST16_PARAM1_VALUE6(0x06) | BF_PXP_HIST16_PARAM1_VALUE7(0x07); __raw_writel(reg_val, pxp->base + HW_PXP_HIST16_PARAM1); reg_val = BF_PXP_HIST16_PARAM2_VALUE8(0x08) | BF_PXP_HIST16_PARAM2_VALUE9(0x09) | BF_PXP_HIST16_PARAM2_VALUE10(0x0A) | BF_PXP_HIST16_PARAM2_VALUE11(0x0B); __raw_writel(reg_val, pxp->base + HW_PXP_HIST16_PARAM2); reg_val = BF_PXP_HIST16_PARAM3_VALUE12(0x0C) | BF_PXP_HIST16_PARAM3_VALUE13(0x0D) | BF_PXP_HIST16_PARAM3_VALUE14(0x0E) | BF_PXP_HIST16_PARAM3_VALUE15(0x0F); __raw_writel(reg_val, pxp->base + HW_PXP_HIST16_PARAM3); return 0; } static int pxp_dma_init(struct pxps *pxp) { struct pxp_dma *pxp_dma = &pxp->pxp_dma; struct dma_device *dma = &pxp_dma->dma; int i; dma_cap_set(DMA_SLAVE, dma->cap_mask); dma_cap_set(DMA_PRIVATE, dma->cap_mask); /* Compulsory common fields */ dma->dev = pxp->dev; dma->device_alloc_chan_resources = pxp_alloc_chan_resources; dma->device_free_chan_resources = pxp_free_chan_resources; dma->device_is_tx_complete = pxp_is_tx_complete; dma->device_issue_pending = pxp_issue_pending; /* Compulsory for DMA_SLAVE fields */ dma->device_prep_slave_sg = pxp_prep_slave_sg; dma->device_terminate_all = pxp_terminate_all; /* Initialize PxP Channels */ INIT_LIST_HEAD(&dma->channels); for (i = 0; i < NR_PXP_VIRT_CHANNEL; i++) { struct pxp_channel *pxp_chan = pxp->channel + i; struct dma_chan *dma_chan = &pxp_chan->dma_chan; spin_lock_init(&pxp_chan->lock); mutex_init(&pxp_chan->chan_mutex); /* Only one EOF IRQ for PxP, shared by all channels */ pxp_chan->eof_irq = pxp->irq; pxp_chan->status = PXP_CHANNEL_FREE; pxp_chan->completed = -ENXIO; snprintf(pxp_chan->eof_name, sizeof(pxp_chan->eof_name), "PXP EOF %d", i); dma_chan->device = &pxp_dma->dma; dma_chan->cookie = 1; dma_chan->chan_id = i; list_add_tail(&dma_chan->device_node, &dma->channels); } return dma_async_device_register(&pxp_dma->dma); } static int pxp_probe(struct platform_device *pdev) { struct pxps *pxp; struct resource *res; int irq; int err = 0; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); if (!res || irq < 0) { err = -ENODEV; goto exit; } pxp = kzalloc(sizeof(*pxp), GFP_KERNEL); if (!pxp) { dev_err(&pdev->dev, "failed to allocate control object\n"); err = -ENOMEM; goto exit; } pxp->dev = &pdev->dev; platform_set_drvdata(pdev, pxp); pxp->irq = irq; spin_lock_init(&pxp->lock); mutex_init(&pxp->mutex); if (!request_mem_region(res->start, resource_size(res), "pxp-mem")) { err = -EBUSY; goto freepxp; } pxp->base = ioremap(res->start, SZ_4K); pxp->pdev = pdev; pxp->clk = clk_get(NULL, "pxp_axi"); clk_enable(pxp->clk); err = pxp_hw_init(pxp); if (err) { dev_err(&pdev->dev, "failed to initialize hardware\n"); goto release; } /* Initialize DMA engine */ err = pxp_dma_init(pxp); if (err < 0) goto err_dma_init; INIT_WORK(&pxp->work, pxpdma_dostart_work); pxp->workqueue = create_singlethread_workqueue("pxp_dma"); exit: return err; err_dma_init: free_irq(pxp->irq, pxp); clk_disable(pxp->clk); release: release_mem_region(res->start, resource_size(res)); freepxp: kfree(pxp); dev_err(&pdev->dev, "Exiting (unsuccessfully) pxp_probe function\n"); return err; } static int __devexit pxp_remove(struct platform_device *pdev) { struct pxps *pxp = platform_get_drvdata(pdev); cancel_work_sync(&pxp->work); kfree(pxp); free_irq(pxp->irq, pxp); clk_disable(pxp->clk); clk_put(pxp->clk); iounmap(pxp->base); kfree(pxp); return 0; } #ifdef CONFIG_PM static int pxp_suspend(struct platform_device *pdev, pm_message_t state) { struct pxps *pxp = platform_get_drvdata(pdev); while (__raw_readl(pxp->base + HW_PXP_CTRL) & BM_PXP_CTRL_ENABLE) ; __raw_writel(BM_PXP_CTRL_SFTRST, pxp->base + HW_PXP_CTRL); clk_disable(pxp->clk); return 0; } static int pxp_resume(struct platform_device *pdev) { struct pxps *pxp = platform_get_drvdata(pdev); clk_enable(pxp->clk); /* Pull PxP out of reset */ __raw_writel(0, pxp->base + HW_PXP_CTRL); return 0; } #else #define pxp_suspend NULL #define pxp_resume NULL #endif static struct platform_driver pxp_driver = { .driver = { .name = "mxc-pxp", }, .probe = pxp_probe, .remove = __exit_p(pxp_remove), .suspend = pxp_suspend, .resume = pxp_resume, }; static int __init pxp_init(void) { return platform_driver_register(&pxp_driver); } subsys_initcall(pxp_init); static void __exit pxp_exit(void) { platform_driver_unregister(&pxp_driver); } module_exit(pxp_exit); MODULE_DESCRIPTION("i.MX PxP driver"); MODULE_AUTHOR("Freescale Semiconductor, Inc."); MODULE_LICENSE("GPL");