Date:2010-05-20 00:14:22 (10 years 4 months ago)
Author:Maarten ter Huurne
Commit:36c6ca21e55a08eda2aad91a81c9563688998a8b
Message:jz4740-slcd-fb: Avoid race conditions between refresh (upload) and framebuffer state changes.

Files: drivers/video/jz4740_slcd_fb.c (6 diffs)

Change Details

drivers/video/jz4740_slcd_fb.c
216216    return 0;
217217}
218218
219static void jzfb_disable_dma(struct jzfb *jzfb)
220{
221    jz4740_dma_disable(jzfb->dma);
222    while (readb(jzfb->base + JZ_REG_SLCD_STATE) & SLCD_STATE_BUSY);
223    writeb(readb(jzfb->base + JZ_REG_SLCD_CTRL) & ~SLCD_CTRL_DMA_EN,
224        jzfb->base + JZ_REG_SLCD_CTRL);
225}
226
219227static struct jz4740_dma_config jzfb_slcd_dma_config = {
220228    .src_width = JZ4740_DMA_WIDTH_32BIT,
221229    .dst_width = JZ4740_DMA_WIDTH_16BIT,
...... 
225233    .mode = JZ4740_DMA_MODE_BLOCK,
226234};
227235
228static void jzfb_refresh_work_complete(
229        struct jz4740_dma_chan *dma, int res, void *dev)
236static void jzfb_upload_frame_dma(struct jzfb *jzfb)
230237{
231    struct jzfb *jzfb = dev_get_drvdata(dev);
232    // TODO: Stick to refresh rate in mode description.
233    int interval = HZ / 60;
238    int num_pixels = 320 * 240;
239
240    jz4740_dma_set_src_addr(jzfb->dma, jzfb->vidmem_phys);
241    jz4740_dma_set_dst_addr(jzfb->dma,
242        CPHYSADDR(jzfb->base + JZ_REG_SLCD_FIFO));
243    jz4740_dma_set_transfer_count(jzfb->dma, num_pixels * 2);
234244
235245    while (readb(jzfb->base + JZ_REG_SLCD_STATE) & SLCD_STATE_BUSY);
236    writeb(readb(jzfb->base + JZ_REG_SLCD_CTRL) & ~SLCD_CTRL_DMA_EN,
246    writeb(readb(jzfb->base + JZ_REG_SLCD_CTRL) | SLCD_CTRL_DMA_EN,
237247        jzfb->base + JZ_REG_SLCD_CTRL);
248    jz4740_dma_enable(jzfb->dma);
249}
238250
239    schedule_delayed_work(&jzfb->refresh_work, interval);
251static void jzfb_upload_frame_cpu(struct jzfb *jzfb)
252{
253    int num_pixels = 320 * 240;
254    int i;
255    uint16_t *p = jzfb->vidmem;
256
257    jzfb_disable_dma(jzfb);
258    for (i = 0; i < num_pixels; i++) {
259        uint16_t rgb = *p++;
260        while (readb(jzfb->base + JZ_REG_SLCD_STATE) & SLCD_STATE_BUSY);
261        writel(SLCD_DATA_RS_DATA | rgb, jzfb->base + JZ_REG_SLCD_DATA);
262    }
240263}
241264
242265static void jzfb_refresh_work(struct work_struct *work)
243266{
244267    struct jzfb *jzfb = container_of(work, struct jzfb, refresh_work.work);
245    int num_pixels = 320 * 240;
246268
247    if (1) {
248        /* Transfer frame by DMA. */
249        jz4740_dma_set_src_addr(jzfb->dma, jzfb->vidmem_phys);
250        jz4740_dma_set_dst_addr(jzfb->dma,
251            CPHYSADDR(jzfb->base + JZ_REG_SLCD_FIFO));
252        jz4740_dma_set_transfer_count(jzfb->dma, num_pixels * 2);
253
254        while (readb(jzfb->base + JZ_REG_SLCD_STATE) & SLCD_STATE_BUSY);
255        writeb(readb(jzfb->base + JZ_REG_SLCD_CTRL) | SLCD_CTRL_DMA_EN,
256            jzfb->base + JZ_REG_SLCD_CTRL);
257        jz4740_dma_enable(jzfb->dma);
258    } else {
259        /* Transfer frame by CPU. */
260        int i;
261        uint16_t *p = jzfb->vidmem;
262
263        for (i = 0; i < num_pixels; i++) {
264            uint16_t rgb = *p++;
265            while (readb(jzfb->base + JZ_REG_SLCD_STATE)
266                & SLCD_STATE_BUSY);
267            writel(SLCD_DATA_RS_DATA | rgb,
268                   jzfb->base + JZ_REG_SLCD_DATA);
269    mutex_lock(&jzfb->lock);
270    if (jzfb->is_enabled) {
271        if (1) {
272            jzfb_upload_frame_dma(jzfb);
273            /* The DMA complete callback will reschedule. */
274        } else {
275            jzfb_upload_frame_cpu(jzfb);
276            schedule_delayed_work(&jzfb->refresh_work, HZ / 10);
269277        }
270278    }
279    mutex_unlock(&jzfb->lock);
280}
281
282static void jzfb_refresh_work_complete(
283        struct jz4740_dma_chan *dma, int res, void *dev)
284{
285    struct jzfb *jzfb = dev_get_drvdata(dev);
286    // TODO: Stick to refresh rate in mode description.
287    int interval = HZ / 60;
288
289    schedule_delayed_work(&jzfb->refresh_work, interval);
271290}
272291
273292static int jzfb_set_par(struct fb_info *info)
...... 
411430        jz_gpio_bulk_resume(jz_slcd_data_pins,
412431                    jzfb_num_data_pins(jzfb));
413432    }
433    jzfb_disable_dma(jzfb);
414434    jzfb->panel->enable(jzfb);
415435
416436    // TODO: SLCD equivalents:
...... 
430450
431451static void jzfb_disable(struct jzfb *jzfb)
432452{
433    cancel_delayed_work_sync(&jzfb->refresh_work);
453    /* It is safe but wasteful to call refresh_work() while disabled. */
454    cancel_delayed_work(&jzfb->refresh_work);
434455
435    /* Abort any DMA transfer that might be in progress. */
436    jz4740_dma_disable(jzfb->dma);
437    while (readb(jzfb->base + JZ_REG_SLCD_STATE) & SLCD_STATE_BUSY);
438    writeb(readb(jzfb->base + JZ_REG_SLCD_CTRL) & ~SLCD_CTRL_DMA_EN,
439        jzfb->base + JZ_REG_SLCD_CTRL);
456    /* Abort any DMA transfer that might be in progress and allow direct
457       writes to the panel. */
458    jzfb_disable_dma(jzfb);
440459
441460    jzfb->panel->disable(jzfb);
442461    jz_gpio_bulk_suspend(jz_slcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb));
...... 
666685        ret = -ENOENT;
667686        goto err_free_devmem;
668687    }
688    jzfb_disable_dma(jzfb);
669689    jzfb->panel->init(jzfb);
670690    jzfb->panel->enable(jzfb);
671691
...... 
708728{
709729    struct jzfb *jzfb = platform_get_drvdata(pdev);
710730
711    cancel_delayed_work_sync(&jzfb->refresh_work);
712
713731    jzfb_blank(FB_BLANK_POWERDOWN, jzfb->fb);
714732
733    /* Blanking will prevent future refreshes from behind scheduled.
734       Now wait for a possible refresh in progress to finish. */
735    cancel_delayed_work_sync(&jzfb->refresh_work);
736
715737    jzfb->panel->exit(jzfb);
716738
717739    jzfb_free_gpio_pins(jzfb);

Archive Download the corresponding diff file



interactive