enum repaper_model { /* 0 is reserved to avoid clashing with NULL */
E1144CS021 = 1,
E1190CS021,
E2200CS021,
E2271CS021,
};
enum repaper_stage { /* Image pixel -> Display pixel */
REPAPER_COMPENSATE, /* B -> W, W -> B (Current Image) */
REPAPER_WHITE, /* B -> N, W -> W (Current Image) */
REPAPER_INVERSE, /* B -> N, W -> B (New Image) */
REPAPER_NORMAL /* B -> B, W -> W (New Image) */
};
switch (stage) { case REPAPER_COMPENSATE: /* B -> W, W -> B (Current) */
pixels = 0xaa | ((pixels ^ 0xaa) >> 1); break; case REPAPER_WHITE: /* B -> N, W -> W (Current) */
pixels = 0x55 + ((pixels ^ 0xaa) >> 1); break; case REPAPER_INVERSE: /* B -> N, W -> B (New) */
pixels = 0x55 | (pixels ^ 0xaa); break; case REPAPER_NORMAL: /* B -> B, W -> W (New) */
pixels = 0xaa | (pixels >> 1); break;
}
switch (stage) { case REPAPER_COMPENSATE: /* B -> W, W -> B (Current) */
pixels = 0xaa | (pixels ^ 0x55); break; case REPAPER_WHITE: /* B -> N, W -> W (Current) */
pixels = 0x55 + (pixels ^ 0x55); break; case REPAPER_INVERSE: /* B -> N, W -> B (New) */
pixels = 0x55 | ((pixels ^ 0x55) << 1); break; case REPAPER_NORMAL: /* B -> B, W -> W (New) */
pixels = 0xaa | pixels; break;
}
switch (stage) { case REPAPER_COMPENSATE: /* B -> W, W -> B (Current) */
pixels = 0xaaaa | (pixels ^ 0x5555); break; case REPAPER_WHITE: /* B -> N, W -> W (Current) */
pixels = 0x5555 + (pixels ^ 0x5555); break; case REPAPER_INVERSE: /* B -> N, W -> B (New) */
pixels = 0x5555 | ((pixels ^ 0x5555) << 1); break; case REPAPER_NORMAL: /* B -> B, W -> W (New) */
pixels = 0xaaaa | pixels; break;
}
/* output one line of scan and data bytes to the display */ staticvoid repaper_one_line(struct repaper_epd *epd, unsignedint line, const u8 *data, u8 fixed_value, const u8 *mask, enum repaper_stage stage)
{
u8 *p = epd->line_buffer; unsignedint b;
repaper_spi_mosi_low(epd->spi);
if (epd->pre_border_byte)
*p++ = 0x00;
if (epd->middle_scan) { /* data bytes */
repaper_odd_pixels(epd, &p, data, fixed_value, mask, stage);
/* scan line */ for (b = epd->bytes_per_scan; b > 0; b--) { if (line / 4 == b - 1)
*p++ = 0x03 << (2 * (line & 0x03)); else
*p++ = 0x00;
}
/* data bytes */
repaper_even_pixels(epd, &p, data, fixed_value, mask, stage);
} else { /* * even scan line, but as lines on display are numbered from 1, * line: 1,3,5,...
*/ for (b = 0; b < epd->bytes_per_scan; b++) { if (0 != (line & 0x01) && line / 8 == b)
*p++ = 0xc0 >> (line & 0x06); else
*p++ = 0x00;
}
/* data bytes */
repaper_all_pixels(epd, &p, data, fixed_value, mask, stage);
/* * odd scan line, but as lines on display are numbered from 1, * line: 0,2,4,6,...
*/ for (b = epd->bytes_per_scan; b > 0; b--) { if (0 == (line & 0x01) && line / 8 == b - 1)
*p++ = 0x03 << (line & 0x06); else
*p++ = 0x00;
}
}
switch (epd->border_byte) { case REPAPER_BORDER_BYTE_NONE: break;
case REPAPER_BORDER_BYTE_ZERO:
*p++ = 0x00; break;
case REPAPER_BORDER_BYTE_SET: switch (stage) { case REPAPER_COMPENSATE: case REPAPER_WHITE: case REPAPER_INVERSE:
*p++ = 0x00; break; case REPAPER_NORMAL:
*p++ = 0xaa; break;
} break;
}
repaper_write_buf(epd->spi, 0x0a, epd->line_buffer,
p - epd->line_buffer);
/* Output data to panel */
repaper_write_val(epd->spi, 0x02, 0x07);
/* * An extra frame write is needed if pixels are set in the bottom line, * or else grey lines rises up from the pixels
*/ if (epd->pre_border_byte) { unsignedint x;
for (x = 0; x < (fb->width / 8); x++) if (buf[x + (fb->width * (fb->height - 1) / 8)]) {
repaper_frame_data_repeat(epd, buf,
epd->current_frame,
REPAPER_NORMAL); break;
}
}
staticvoid power_off(struct repaper_epd *epd)
{ /* Turn off power and all signals */
gpiod_set_value_cansleep(epd->reset, 0);
gpiod_set_value_cansleep(epd->panel_on, 0); if (epd->border)
gpiod_set_value_cansleep(epd->border, 0);
/* Ensure SPI MOSI and CLOCK are Low before CS Low */
repaper_spi_mosi_low(epd->spi);
/* Power up sequence */
gpiod_set_value_cansleep(epd->reset, 0);
gpiod_set_value_cansleep(epd->panel_on, 0);
gpiod_set_value_cansleep(epd->discharge, 0); if (epd->border)
gpiod_set_value_cansleep(epd->border, 0);
repaper_spi_mosi_low(spi);
usleep_range(5000, 10000);
gpiod_set_value_cansleep(epd->panel_on, 1); /* * This delay comes from the repaper.org userspace driver, it's not * mentioned in the datasheet.
*/
usleep_range(10000, 15000);
gpiod_set_value_cansleep(epd->reset, 1); if (epd->border)
gpiod_set_value_cansleep(epd->border, 1);
usleep_range(5000, 10000);
gpiod_set_value_cansleep(epd->reset, 0);
usleep_range(5000, 10000);
gpiod_set_value_cansleep(epd->reset, 1);
usleep_range(5000, 10000);
/* Wait for COG to become ready */ for (i = 100; i > 0; i--) { if (!gpiod_get_value_cansleep(epd->busy)) break;
usleep_range(10, 100);
}
if (!i) {
DRM_DEV_ERROR(dev, "timeout waiting for panel to become ready.\n");
power_off(epd); goto out_exit;
}
repaper_read_id(spi);
ret = repaper_read_id(spi); if (ret != REPAPER_RID_G2_COG_ID) { if (ret < 0)
dev_err(dev, "failed to read chip (%d)\n", ret); else
dev_err(dev, "wrong COG ID 0x%02x\n", ret);
power_off(epd); goto out_exit;
}
/* * This callback is not protected by drm_dev_enter/exit since we want to * turn off the display on regular driver unload. It's highly unlikely * that the underlying SPI controller is gone should this be called after * unplug.
*/
DRM_DEBUG_DRIVER("\n");
/* Nothing frame */ for (line = 0; line < epd->height; line++)
repaper_one_line(epd, 0x7fffu, NULL, 0x00, NULL,
REPAPER_COMPENSATE);
match = device_get_match_data(dev); if (match) {
model = (enum repaper_model)(uintptr_t)match;
} else {
spi_id = spi_get_device_id(spi);
model = (enum repaper_model)spi_id->driver_data;
}
/* The SPI device is used to allocate dma memory */ if (!dev->coherent_dma_mask) {
ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); if (ret) {
dev_warn(dev, "Failed to set dma mask %d\n", ret); return ret;
}
}
ret = drmm_mode_config_init(drm); if (ret) return ret;
drm->mode_config.funcs = &repaper_mode_config_funcs;
epd->spi = spi;
epd->panel_on = devm_gpiod_get(dev, "panel-on", GPIOD_OUT_LOW); if (IS_ERR(epd->panel_on)) {
ret = PTR_ERR(epd->panel_on); if (ret != -EPROBE_DEFER)
DRM_DEV_ERROR(dev, "Failed to get gpio 'panel-on'\n"); return ret;
}
epd->discharge = devm_gpiod_get(dev, "discharge", GPIOD_OUT_LOW); if (IS_ERR(epd->discharge)) {
ret = PTR_ERR(epd->discharge); if (ret != -EPROBE_DEFER)
DRM_DEV_ERROR(dev, "Failed to get gpio 'discharge'\n"); return ret;
}
epd->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(epd->reset)) {
ret = PTR_ERR(epd->reset); if (ret != -EPROBE_DEFER)
DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n"); return ret;
}
epd->busy = devm_gpiod_get(dev, "busy", GPIOD_IN); if (IS_ERR(epd->busy)) {
ret = PTR_ERR(epd->busy); if (ret != -EPROBE_DEFER)
DRM_DEV_ERROR(dev, "Failed to get gpio 'busy'\n"); return ret;
}
if (!device_property_read_string(dev, "pervasive,thermal-zone",
&thermal_zone)) {
epd->thermal = thermal_zone_get_zone_by_name(thermal_zone); if (IS_ERR(epd->thermal)) {
DRM_DEV_ERROR(dev, "Failed to get thermal zone: %s\n", thermal_zone); return PTR_ERR(epd->thermal);
}
}
case E2271CS021:
epd->border = devm_gpiod_get(dev, "border", GPIOD_OUT_LOW); if (IS_ERR(epd->border)) {
ret = PTR_ERR(epd->border); if (ret != -EPROBE_DEFER)
DRM_DEV_ERROR(dev, "Failed to get gpio 'border'\n"); return ret;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.